summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/thermal.c9
-rw-r--r--drivers/ata/Kconfig18
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/acard-ahci.c4
-rw-r--r--drivers/ata/ahci.c104
-rw-r--r--drivers/ata/ahci.h6
-rw-r--r--drivers/ata/ahci_brcmstb.c322
-rw-r--r--drivers/ata/ahci_ceva.c238
-rw-r--r--drivers/ata/ahci_mvebu.c22
-rw-r--r--drivers/ata/ahci_platform.c1
-rw-r--r--drivers/ata/ahci_xgene.c103
-rw-r--r--drivers/ata/libahci.c105
-rw-r--r--drivers/ata/libahci_platform.c4
-rw-r--r--drivers/ata/libata-core.c13
-rw-r--r--drivers/ata/libata-eh.c13
-rw-r--r--drivers/ata/libata-transport.c22
-rw-r--r--drivers/ata/pata_hpt366.c4
-rw-r--r--drivers/ata/pata_samsung_cf.c2
-rw-r--r--drivers/ata/sata_highbank.c3
-rw-r--r--drivers/ata/sata_nv.c2
-rw-r--r--drivers/block/drbd/drbd_int.h1
-rw-r--r--drivers/block/drbd/drbd_main.c10
-rw-r--r--drivers/block/loop.c82
-rw-r--r--drivers/block/loop.h3
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c228
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h10
-rw-r--r--drivers/block/nbd.c52
-rw-r--r--drivers/block/null_blk.c14
-rw-r--r--drivers/block/nvme-core.c812
-rw-r--r--drivers/block/nvme-scsi.c1230
-rw-r--r--drivers/block/paride/pd.c4
-rw-r--r--drivers/block/pktcdvd.c1
-rw-r--r--drivers/block/ps3vram.c34
-rw-r--r--drivers/block/sx8.c4
-rw-r--r--drivers/block/virtio_blk.c6
-rw-r--r--drivers/char/raw.c1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/h8300/Makefile2
-rw-r--r--drivers/clk/h8300/clk-div.c53
-rw-r--r--drivers/clk/h8300/clk-h8s2678.c146
-rw-r--r--drivers/clocksource/Kconfig7
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/h8300_timer16.c254
-rw-r--r--drivers/clocksource/h8300_timer8.c313
-rw-r--r--drivers/clocksource/h8300_tpu.c207
-rw-r--r--drivers/dma/sh/rcar-dmac.c37
-rw-r--r--drivers/edac/edac_mc_sysfs.c5
-rw-r--r--drivers/edac/sb_edac.c215
-rw-r--r--drivers/firmware/dmi-sysfs.c17
-rw-r--r--drivers/firmware/dmi_scan.c123
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h3
-rw-r--r--drivers/gpu/drm/i915/intel_audio.c27
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c2
-rw-r--r--drivers/i2c/busses/Kconfig32
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-at91.c362
-rw-r--r--drivers/i2c/busses/i2c-axxia.c41
-rw-r--r--drivers/i2c/busses/i2c-bcm-iproc.c57
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c11
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c694
-rw-r--r--drivers/i2c/busses/i2c-davinci.c80
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c35
-rw-r--r--drivers/i2c/busses/i2c-imx.c2
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c731
-rw-r--r--drivers/i2c/busses/i2c-mxs.c2
-rw-r--r--drivers/i2c/busses/i2c-octeon.c7
-rw-r--r--drivers/i2c/busses/i2c-omap.c74
-rw-r--r--drivers/i2c/busses/i2c-rcar.c10
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c49
-rw-r--r--drivers/i2c/busses/i2c-tegra.c11
-rw-r--r--drivers/i2c/busses/i2c-xgene-slimpro.c469
-rw-r--r--drivers/i2c/busses/i2c-xiic.c1
-rw-r--r--drivers/i2c/i2c-core.c63
-rw-r--r--drivers/i2c/i2c-mux.c3
-rw-r--r--drivers/i2c/i2c-smbus.c2
-rw-r--r--drivers/i2c/muxes/Kconfig5
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca9541.c4
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c2
-rw-r--r--drivers/ide/ide-atapi.c10
-rw-r--r--drivers/ide/ide-cd.c10
-rw-r--r--drivers/ide/ide-cd_ioctl.c2
-rw-r--r--drivers/ide/ide-devsets.c2
-rw-r--r--drivers/ide/ide-eh.c4
-rw-r--r--drivers/ide/ide-floppy.c8
-rw-r--r--drivers/ide/ide-io.c12
-rw-r--r--drivers/ide/ide-ioctls.c2
-rw-r--r--drivers/ide/ide-park.c4
-rw-r--r--drivers/ide/ide-pm.c56
-rw-r--r--drivers/ide/ide-tape.c6
-rw-r--r--drivers/ide/ide-taskfile.c2
-rw-r--r--drivers/input/touchscreen/Kconfig3
-rw-r--r--drivers/input/touchscreen/sur40.c46
-rw-r--r--drivers/irqchip/Kconfig8
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/irq-renesas-h8300h.c95
-rw-r--r--drivers/irqchip/irq-renesas-h8s.c101
-rw-r--r--drivers/mailbox/Kconfig10
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/arm_mhu.c2
-rw-r--r--drivers/mailbox/bcm2835-mailbox.c217
-rw-r--r--drivers/mailbox/mailbox-altera.c2
-rw-r--r--drivers/mailbox/mailbox.c40
-rw-r--r--drivers/mailbox/omap-mailbox.c8
-rw-r--r--drivers/mailbox/pcc.c2
-rw-r--r--drivers/md/Kconfig12
-rw-r--r--drivers/md/Makefile2
-rw-r--r--drivers/md/bcache/io.c2
-rw-r--r--drivers/md/bcache/request.c3
-rw-r--r--drivers/md/dm-bio-prison.c26
-rw-r--r--drivers/md/dm-bio-prison.h13
-rw-r--r--drivers/md/dm-cache-metadata.c133
-rw-r--r--drivers/md/dm-cache-metadata.h10
-rw-r--r--drivers/md/dm-cache-policy-cleaner.c6
-rw-r--r--drivers/md/dm-cache-policy-internal.h52
-rw-r--r--drivers/md/dm-cache-policy-mq.c93
-rw-r--r--drivers/md/dm-cache-policy-smq.c1791
-rw-r--r--drivers/md/dm-cache-policy.h30
-rw-r--r--drivers/md/dm-cache-target.c838
-rw-r--r--drivers/md/dm-crypt.c30
-rw-r--r--drivers/md/dm-log-writes.c4
-rw-r--r--drivers/md/dm-raid.c225
-rw-r--r--drivers/md/dm-raid1.c77
-rw-r--r--drivers/md/dm-snap.c1
-rw-r--r--drivers/md/dm-stats.c341
-rw-r--r--drivers/md/dm-stats.h4
-rw-r--r--drivers/md/dm-stripe.c4
-rw-r--r--drivers/md/dm-table.c29
-rw-r--r--drivers/md/dm-thin-metadata.c124
-rw-r--r--drivers/md/dm-thin-metadata.h11
-rw-r--r--drivers/md/dm-thin.c615
-rw-r--r--drivers/md/dm-verity.c2
-rw-r--r--drivers/md/dm.c341
-rw-r--r--drivers/md/dm.h6
-rw-r--r--drivers/md/md.h1
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c6
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h1
-rw-r--r--drivers/md/persistent-data/dm-btree-remove.c127
-rw-r--r--drivers/md/persistent-data/dm-btree.h9
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c50
-rw-r--r--drivers/md/raid1.c4
-rw-r--r--drivers/md/raid10.c2
-rw-r--r--drivers/media/Kconfig2
-rw-r--r--drivers/media/common/b2c2/Kconfig1
-rw-r--r--drivers/media/common/b2c2/flexcop-common.h1
-rw-r--r--drivers/media/common/b2c2/flexcop-fe-tuner.c63
-rw-r--r--drivers/media/common/b2c2/flexcop-hw-filter.c16
-rw-r--r--drivers/media/common/b2c2/flexcop-misc.c1
-rw-r--r--drivers/media/common/b2c2/flexcop-reg.h1
-rw-r--r--drivers/media/common/siano/smscoreapi.h3
-rw-r--r--drivers/media/common/siano/smsdvb-main.c6
-rw-r--r--drivers/media/common/siano/smsdvb.h2
-rw-r--r--drivers/media/common/siano/smsir.c2
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c78
-rw-r--r--drivers/media/dvb-core/dvb_frontend.h45
-rw-r--r--drivers/media/dvb-frontends/Kconfig13
-rw-r--r--drivers/media/dvb-frontends/Makefile1
-rw-r--r--drivers/media/dvb-frontends/a8293.c89
-rw-r--r--drivers/media/dvb-frontends/a8293.h15
-rw-r--r--drivers/media/dvb-frontends/af9013.c8
-rw-r--r--drivers/media/dvb-frontends/af9033.c4
-rw-r--r--drivers/media/dvb-frontends/as102_fe.c4
-rw-r--r--drivers/media/dvb-frontends/atbm8830.c3
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c4
-rw-r--r--drivers/media/dvb-frontends/au8522_priv.h2
-rw-r--r--drivers/media/dvb-frontends/bcm3510.c6
-rw-r--r--drivers/media/dvb-frontends/cx22700.c9
-rw-r--r--drivers/media/dvb-frontends/cx22702.c2
-rw-r--r--drivers/media/dvb-frontends/cx24110.c19
-rw-r--r--drivers/media/dvb-frontends/cx24116.c46
-rw-r--r--drivers/media/dvb-frontends/cx24117.c42
-rw-r--r--drivers/media/dvb-frontends/cx24120.c1595
-rw-r--r--drivers/media/dvb-frontends/cx24120.h58
-rw-r--r--drivers/media/dvb-frontends/cx24123.c18
-rw-r--r--drivers/media/dvb-frontends/cx24123.h2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_c.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c5
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_priv.h8
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t2.c2
-rw-r--r--drivers/media/dvb-frontends/dib0070.c575
-rw-r--r--drivers/media/dvb-frontends/dib0090.c4
-rw-r--r--drivers/media/dvb-frontends/dib3000mb.c7
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.c20
-rw-r--r--drivers/media/dvb-frontends/dib7000m.c2
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c6
-rw-r--r--drivers/media/dvb-frontends/dib8000.c10
-rw-r--r--drivers/media/dvb-frontends/dib8000.h2
-rw-r--r--drivers/media/dvb-frontends/dib9000.c4
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c42
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c2
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c11
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.h2
-rw-r--r--drivers/media/dvb-frontends/ds3000.c13
-rw-r--r--drivers/media/dvb-frontends/dvb_dummy_fe.c9
-rw-r--r--drivers/media/dvb-frontends/ec100.c2
-rw-r--r--drivers/media/dvb-frontends/hd29l2.c2
-rw-r--r--drivers/media/dvb-frontends/hd29l2_priv.h2
-rw-r--r--drivers/media/dvb-frontends/isl6405.c3
-rw-r--r--drivers/media/dvb-frontends/isl6421.c6
-rw-r--r--drivers/media/dvb-frontends/l64781.c2
-rw-r--r--drivers/media/dvb-frontends/lg2160.c2
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.c4
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c11
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c8
-rw-r--r--drivers/media/dvb-frontends/lgs8gl5.c2
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.c3
-rw-r--r--drivers/media/dvb-frontends/lnbp21.c4
-rw-r--r--drivers/media/dvb-frontends/lnbp22.c3
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c1275
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.h67
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h20
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c19
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c7
-rw-r--r--drivers/media/dvb-frontends/mb86a16.h3
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c6
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.h2
-rw-r--r--drivers/media/dvb-frontends/mt312.c17
-rw-r--r--drivers/media/dvb-frontends/mt352.c2
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c2
-rw-r--r--drivers/media/dvb-frontends/nxt6000.c12
-rw-r--r--drivers/media/dvb-frontends/or51132.c6
-rw-r--r--drivers/media/dvb-frontends/or51211.c2
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c2
-rw-r--r--drivers/media/dvb-frontends/rtl2830_priv.h2
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c10
-rw-r--r--drivers/media/dvb-frontends/rtl2832.h2
-rw-r--r--drivers/media/dvb-frontends/rtl2832_priv.h51
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c120
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.h1
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c6
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c6
-rw-r--r--drivers/media/dvb-frontends/s5h1420.c43
-rw-r--r--drivers/media/dvb-frontends/s5h1432.c4
-rw-r--r--drivers/media/dvb-frontends/s921.c6
-rw-r--r--drivers/media/dvb-frontends/s921.h2
-rw-r--r--drivers/media/dvb-frontends/si2165.c2
-rw-r--r--drivers/media/dvb-frontends/si2168.c144
-rw-r--r--drivers/media/dvb-frontends/si2168.h3
-rw-r--r--drivers/media/dvb-frontends/si2168_priv.h6
-rw-r--r--drivers/media/dvb-frontends/si21xx.c10
-rw-r--r--drivers/media/dvb-frontends/sp8870.c3
-rw-r--r--drivers/media/dvb-frontends/sp887x.c2
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c8
-rw-r--r--drivers/media/dvb-frontends/stv0288.c39
-rw-r--r--drivers/media/dvb-frontends/stv0297.c19
-rw-r--r--drivers/media/dvb-frontends/stv0299.c34
-rw-r--r--drivers/media/dvb-frontends/stv0367.c12
-rw-r--r--drivers/media/dvb-frontends/stv0367_priv.h2
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c6
-rw-r--r--drivers/media/dvb-frontends/stv0900_sw.c6
-rw-r--r--drivers/media/dvb-frontends/stv090x.c5
-rw-r--r--drivers/media/dvb-frontends/stv6110.c2
-rw-r--r--drivers/media/dvb-frontends/tc90522.c17
-rw-r--r--drivers/media/dvb-frontends/tda10021.c9
-rw-r--r--drivers/media/dvb-frontends/tda10023.c5
-rw-r--r--drivers/media/dvb-frontends/tda10048.c2
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c5
-rw-r--r--drivers/media/dvb-frontends/tda10071.c117
-rw-r--r--drivers/media/dvb-frontends/tda10071.h29
-rw-r--r--drivers/media/dvb-frontends/tda10071_priv.h11
-rw-r--r--drivers/media/dvb-frontends/tda10086.c13
-rw-r--r--drivers/media/dvb-frontends/tda8083.c38
-rw-r--r--drivers/media/dvb-frontends/ts2020.c591
-rw-r--r--drivers/media/dvb-frontends/ts2020.h17
-rw-r--r--drivers/media/dvb-frontends/ves1820.c6
-rw-r--r--drivers/media/dvb-frontends/ves1x93.c15
-rw-r--r--drivers/media/dvb-frontends/zl10353.c12
-rw-r--r--drivers/media/firewire/firedtv-fe.c8
-rw-r--r--drivers/media/firewire/firedtv.h4
-rw-r--r--drivers/media/i2c/Kconfig4
-rw-r--r--drivers/media/i2c/adp1653.c100
-rw-r--r--drivers/media/i2c/adv7170.c42
-rw-r--r--drivers/media/i2c/adv7175.c42
-rw-r--r--drivers/media/i2c/adv7183.c61
-rw-r--r--drivers/media/i2c/adv7511.c160
-rw-r--r--drivers/media/i2c/adv7604.c192
-rw-r--r--drivers/media/i2c/adv7842.c309
-rw-r--r--drivers/media/i2c/ak881x.c39
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c17
-rw-r--r--drivers/media/i2c/ml86v7667.c29
-rw-r--r--drivers/media/i2c/mt9v011.c53
-rw-r--r--drivers/media/i2c/ov2659.c38
-rw-r--r--drivers/media/i2c/ov7670.c65
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c2
-rw-r--r--drivers/media/i2c/s5k5baf.c4
-rw-r--r--drivers/media/i2c/s5k6aa.c2
-rw-r--r--drivers/media/i2c/saa6752hs.c42
-rw-r--r--drivers/media/i2c/saa7115.c16
-rw-r--r--drivers/media/i2c/saa717x.c20
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c38
-rw-r--r--drivers/media/i2c/soc_camera/imx074.c66
-rw-r--r--drivers/media/i2c/soc_camera/mt9m001.c43
-rw-r--r--drivers/media/i2c/soc_camera/mt9m111.c57
-rw-r--r--drivers/media/i2c/soc_camera/mt9t031.c74
-rw-r--r--drivers/media/i2c/soc_camera/mt9t112.c41
-rw-r--r--drivers/media/i2c/soc_camera/mt9v022.c43
-rw-r--r--drivers/media/i2c/soc_camera/ov2640.c62
-rw-r--r--drivers/media/i2c/soc_camera/ov5642.c60
-rw-r--r--drivers/media/i2c/soc_camera/ov6650.c43
-rw-r--r--drivers/media/i2c/soc_camera/ov772x.c41
-rw-r--r--drivers/media/i2c/soc_camera/ov9640.c32
-rw-r--r--drivers/media/i2c/soc_camera/ov9740.c35
-rw-r--r--drivers/media/i2c/soc_camera/rj54n1cb0c.c66
-rw-r--r--drivers/media/i2c/soc_camera/tw9910.c41
-rw-r--r--drivers/media/i2c/sr030pc30.c62
-rw-r--r--drivers/media/i2c/tvaudio.c2
-rw-r--r--drivers/media/i2c/tvp514x.c55
-rw-r--r--drivers/media/i2c/tvp5150.c30
-rw-r--r--drivers/media/i2c/tvp7002.c48
-rw-r--r--drivers/media/i2c/vs6624.c55
-rw-r--r--drivers/media/pci/Kconfig2
-rw-r--r--drivers/media/pci/Makefile2
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c443
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c5
-rw-r--r--drivers/media/pci/bt8xx/dst.c25
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c138
-rw-r--r--drivers/media/pci/bt8xx/dst_common.h12
-rw-r--r--drivers/media/pci/cobalt/Kconfig18
-rw-r--r--drivers/media/pci/cobalt/Makefile5
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-main.c162
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.c603
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.h22
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa.h41
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.c341
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.h29
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c832
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.h380
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.c128
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.h29
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.c396
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.h25
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.c258
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.h25
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.c341
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.h62
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c1272
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.h22
-rw-r--r--drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h115
-rw-r--r--drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h44
-rw-r--r--drivers/media/pci/cobalt/m00389_cvi_memmap_package.h59
-rw-r--r--drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h44
-rw-r--r--drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h57
-rw-r--r--drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h53
-rw-r--r--drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h88
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c16
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c13
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c4
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c12
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c1
-rw-r--r--drivers/media/pci/cx23885/altera-ci.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c150
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c12
-rw-r--r--drivers/media/pci/cx23885/cx23885.h3
-rw-r--r--drivers/media/pci/cx88/cx88-core.c2
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c12
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c6
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c6
-rw-r--r--drivers/media/pci/cx88/cx88-video.c9
-rw-r--r--drivers/media/pci/cx88/cx88.h6
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c3
-rw-r--r--drivers/media/pci/dm1105/dm1105.c3
-rw-r--r--drivers/media/pci/dt3155/Kconfig13
-rw-r--r--drivers/media/pci/dt3155/Makefile1
-rw-r--r--drivers/media/pci/dt3155/dt3155.c632
-rw-r--r--drivers/media/pci/dt3155/dt3155.h (renamed from drivers/staging/media/dt3155v4l/dt3155v4l.h)64
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c12
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c4
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h3
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c15
-rw-r--r--drivers/media/pci/mantis/hopper_cards.c14
-rw-r--r--drivers/media/pci/mantis/mantis_cards.c94
-rw-r--r--drivers/media/pci/mantis/mantis_common.h33
-rw-r--r--drivers/media/pci/mantis/mantis_dma.c5
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.c12
-rw-r--r--drivers/media/pci/mantis/mantis_input.c110
-rw-r--r--drivers/media/pci/mantis/mantis_input.h24
-rw-r--r--drivers/media/pci/mantis/mantis_pcmcia.c4
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c61
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.c2
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.h3
-rw-r--r--drivers/media/pci/ngene/ngene-core.c10
-rw-r--r--drivers/media/pci/ngene/ngene.h2
-rw-r--r--drivers/media/pci/pt1/pt1.c6
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.c4
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.c4
-rw-r--r--drivers/media/pci/pt3/pt3.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c55
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c150
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c161
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c122
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c55
-rw-r--r--drivers/media/pci/saa7134/saa7134-go7007.c11
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c87
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c59
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c24
-rw-r--r--drivers/media/pci/saa7134/saa7134-tvaudio.c168
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c14
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c43
-rw-r--r--drivers/media/pci/saa7134/saa7134.h6
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c11
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-cards.c188
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c82
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c241
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c13
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-i2c.c9
-rw-r--r--drivers/media/pci/saa7164/saa7164-reg.h2
-rw-r--r--drivers/media/pci/saa7164/saa7164-types.h2
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c13
-rw-r--r--drivers/media/pci/saa7164/saa7164.h8
-rw-r--r--drivers/media/pci/smipcie/smipcie.c1
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c3
-rw-r--r--drivers/media/pci/ttpci/av7110.c18
-rw-r--r--drivers/media/pci/ttpci/av7110.h27
-rw-r--r--drivers/media/pci/ttpci/budget-core.c3
-rw-r--r--drivers/media/pci/ttpci/budget-patch.c15
-rw-r--r--drivers/media/pci/ttpci/budget.c12
-rw-r--r--drivers/media/pci/ttpci/budget.h2
-rw-r--r--drivers/media/pci/zoran/zoran_device.c13
-rw-r--r--drivers/media/platform/Kconfig10
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c35
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c40
-rw-r--r--drivers/media/platform/coda/coda-bit.c4
-rw-r--r--drivers/media/platform/coda/coda-common.c27
-rw-r--r--drivers/media/platform/coda/coda.h3
-rw-r--r--drivers/media/platform/coda/trace.h2
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c9
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c19
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c2
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig1
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c2
-rw-r--r--drivers/media/platform/fsl-viu.c2
-rw-r--r--drivers/media/platform/m2m-deinterlace.c1
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c13
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c480
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h3
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c1
-rw-r--r--drivers/media/platform/omap/omap_vout.c10
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c4
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c13
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.c2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c5
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c4
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c14
-rw-r--r--drivers/media/platform/s5p-tv/mixer_drv.c15
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c14
-rw-r--r--drivers/media/platform/sh_vou.c75
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c74
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c113
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c105
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c106
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c99
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c113
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c115
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c35
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c30
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c24
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.c37
-rw-r--r--drivers/media/platform/sti/bdisp/Makefile3
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-debug.c679
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-filter.h346
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-hw.c823
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-reg.h235
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-v4l2.c1416
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp.h216
-rw-r--r--drivers/media/platform/via-camera.c19
-rw-r--r--drivers/media/platform/vim2m.c12
-rw-r--r--drivers/media/platform/vivid/vivid-core.c20
-rw-r--r--drivers/media/platform/vivid/vivid-core.h6
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.c139
-rw-r--r--drivers/media/platform/vivid/vivid-radio-rx.c2
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.c96
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.h2
-rw-r--r--drivers/media/platform/vivid/vivid-tpg-colors.c478
-rw-r--r--drivers/media/platform/vivid/vivid-tpg-colors.h4
-rw-r--r--drivers/media/platform/vivid/vivid-tpg.c313
-rw-r--r--drivers/media/platform/vivid/vivid-tpg.h20
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c31
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.c68
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c7
-rw-r--r--drivers/media/platform/xilinx/Kconfig2
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c4
-rw-r--r--drivers/media/radio/radio-si476x.c4
-rw-r--r--drivers/media/radio/radio-timb.c4
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c9
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c6
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h8
-rw-r--r--drivers/media/radio/si4713/si4713.c4
-rw-r--r--drivers/media/radio/wl128x/Kconfig4
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h2
-rw-r--r--drivers/media/rc/fintek-cir.c1
-rw-r--r--drivers/media/rc/gpio-ir-recv.c4
-rw-r--r--drivers/media/rc/ir-hix5hd2.c8
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c116
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c122
-rw-r--r--drivers/media/rc/ir-sony-decoder.c28
-rw-r--r--drivers/media/rc/keymaps/Makefile4
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-ts35.c76
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c88
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c86
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c98
-rw-r--r--drivers/media/rc/nuvoton-cir.c127
-rw-r--r--drivers/media/rc/nuvoton-cir.h1
-rw-r--r--drivers/media/rc/rc-core-priv.h36
-rw-r--r--drivers/media/rc/rc-ir-raw.c139
-rw-r--r--drivers/media/rc/rc-loopback.c36
-rw-r--r--drivers/media/rc/rc-main.c9
-rw-r--r--drivers/media/rc/redrat3.c7
-rw-r--r--drivers/media/rc/st_rc.c12
-rw-r--r--drivers/media/rc/streamzap.c6
-rw-r--r--drivers/media/tuners/Kconfig5
-rw-r--r--drivers/media/tuners/e4000.c592
-rw-r--r--drivers/media/tuners/e4000.h1
-rw-r--r--drivers/media/tuners/e4000_priv.h11
-rw-r--r--drivers/media/tuners/fc0013.c2
-rw-r--r--drivers/media/tuners/fc2580.c781
-rw-r--r--drivers/media/tuners/fc2580.h40
-rw-r--r--drivers/media/tuners/fc2580_priv.h36
-rw-r--r--drivers/media/tuners/msi001.c267
-rw-r--r--drivers/media/tuners/qt1010.c8
-rw-r--r--drivers/media/tuners/r820t.c4
-rw-r--r--drivers/media/tuners/si2157.c44
-rw-r--r--drivers/media/tuners/si2157.h6
-rw-r--r--drivers/media/tuners/si2157_priv.h2
-rw-r--r--drivers/media/tuners/tua9001.c331
-rw-r--r--drivers/media/tuners/tua9001.h35
-rw-r--r--drivers/media/tuners/tua9001_priv.h19
-rw-r--r--drivers/media/tuners/tuner-i2c.h10
-rw-r--r--drivers/media/tuners/tuner-xc2028.c2
-rw-r--r--drivers/media/usb/as102/as102_drv.c1
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c2
-rw-r--r--drivers/media/usb/au0828/au0828-core.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c21
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c44
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c56
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c30
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c3
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c26
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h1
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c58
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c18
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c112
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c14
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c193
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.h5
-rw-r--r--drivers/media/usb/dvb-usb/af9005-fe.c7
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c3
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c1
-rw-r--r--drivers/media/usb/dvb-usb/dib0700.h2
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c70
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c145
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u-fe.c7
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c55
-rw-r--r--drivers/media/usb/dvb-usb/friio-fe.c3
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk-fe.c13
-rw-r--r--drivers/media/usb/dvb-usb/opera1.c3
-rw-r--r--drivers/media/usb/dvb-usb/technisat-usb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp702x-fe.c17
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c7
-rw-r--r--drivers/media/usb/dvb-usb/vp7045-fe.c3
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c12
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c216
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c1
-rw-r--r--drivers/media/usb/go7007/go7007-driver.c3
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c4
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c12
-rw-r--r--drivers/media/usb/go7007/s2250-board.c18
-rw-r--r--drivers/media/usb/gspca/benq.c4
-rw-r--r--drivers/media/usb/gspca/sn9c2028.c241
-rw-r--r--drivers/media/usb/gspca/sn9c2028.h18
-rw-r--r--drivers/media/usb/gspca/sonixj.c2
-rw-r--r--drivers/media/usb/gspca/stk014.c2
-rw-r--r--drivers/media/usb/gspca/xirlink_cit.c12
-rw-r--r--drivers/media/usb/gspca/zc3xx.c16
-rw-r--r--drivers/media/usb/msi2500/msi2500.c655
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.c3
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c35
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.c30
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ioread.c24
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c3
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c5
-rw-r--r--drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c9
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c4
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.c10
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c12
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c4
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c17
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c2
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c12
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c16
-rw-r--r--drivers/media/usb/uvc/uvc_video.c8
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h7
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c3
-rw-r--r--drivers/media/v4l2-core/Kconfig2
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c117
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c214
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c38
-rw-r--r--drivers/media/v4l2-core/v4l2-of.c100
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c63
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-contig.c6
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-sg.c22
-rw-r--r--drivers/media/v4l2-core/videobuf2-vmalloc.c6
-rw-r--r--drivers/mfd/mt6397-core.c18
-rw-r--r--drivers/mmc/host/android-goldfish.c2
-rw-r--r--drivers/mtd/devices/block2mtd.c1
-rw-r--r--drivers/mtd/ubi/block.c16
-rw-r--r--drivers/mtd/ubi/build.c107
-rw-r--r--drivers/mtd/ubi/fastmap.c83
-rw-r--r--drivers/mtd/ubi/ubi.h2
-rw-r--r--drivers/mtd/ubi/vmt.c98
-rw-r--r--drivers/mtd/ubi/vtbl.c45
-rw-r--r--drivers/mtd/ubi/wl.c2
-rw-r--r--drivers/platform/x86/acerhdf.c3
-rw-r--r--drivers/regulator/arizona-ldo1.c5
-rw-r--r--drivers/rtc/Kconfig104
-rw-r--r--drivers/rtc/Makefile44
-rw-r--r--drivers/rtc/interface.c49
-rw-r--r--drivers/rtc/rtc-ab8500.c2
-rw-r--r--drivers/rtc/rtc-at32ap700x.c2
-rw-r--r--drivers/rtc/rtc-ds1216.c4
-rw-r--r--drivers/rtc/rtc-ds1286.c4
-rw-r--r--drivers/rtc/rtc-ds1307.c12
-rw-r--r--drivers/rtc/rtc-ds1672.c1
-rw-r--r--drivers/rtc/rtc-efi.c43
-rw-r--r--drivers/rtc/rtc-ep93xx.c6
-rw-r--r--drivers/rtc/rtc-gemini.c175
-rw-r--r--drivers/rtc/rtc-hid-sensor-time.c2
-rw-r--r--drivers/rtc/rtc-hym8563.c18
-rw-r--r--drivers/rtc/rtc-imxdi.c438
-rw-r--r--drivers/rtc/rtc-isl1208.c9
-rw-r--r--drivers/rtc/rtc-max6900.c1
-rw-r--r--drivers/rtc/rtc-max77686.c1
-rw-r--r--drivers/rtc/rtc-max77802.c1
-rw-r--r--drivers/rtc/rtc-max8998.c1
-rw-r--r--drivers/rtc/rtc-mc13xxx.c2
-rw-r--r--drivers/rtc/rtc-mt6397.c395
-rw-r--r--drivers/rtc/rtc-mv.c13
-rw-r--r--drivers/rtc/rtc-mxc.c2
-rw-r--r--drivers/rtc/rtc-palmas.c2
-rw-r--r--drivers/rtc/rtc-pcf8563.c21
-rw-r--r--drivers/rtc/rtc-s3c.c14
-rw-r--r--drivers/rtc/rtc-snvs.c30
-rw-r--r--drivers/rtc/rtc-spear.c7
-rw-r--r--drivers/rtc/rtc-sunxi.c32
-rw-r--r--drivers/rtc/rtc-v3020.c41
-rw-r--r--drivers/rtc/systohc.c2
-rw-r--r--drivers/s390/block/dasd_genhd.c19
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h4
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.c3
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c1
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h2
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c18
-rw-r--r--drivers/staging/media/dt3155v4l/Kconfig29
-rw-r--r--drivers/staging/media/dt3155v4l/Makefile1
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c981
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c97
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c75
-rw-r--r--drivers/staging/media/mn88472/mn88472.c6
-rw-r--r--drivers/staging/media/mn88472/mn88472_priv.h2
-rw-r--r--drivers/staging/media/mn88473/mn88473.c2
-rw-r--r--drivers/staging/media/mn88473/mn88473_priv.h2
-rw-r--r--drivers/staging/media/omap4iss/iss.c2
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c18
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c30
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c10
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c8
-rw-r--r--drivers/thermal/Kconfig68
-rw-r--r--drivers/thermal/Makefile5
-rw-r--r--drivers/thermal/cpu_cooling.c585
-rw-r--r--drivers/thermal/db8500_thermal.c2
-rw-r--r--drivers/thermal/fair_share.c41
-rw-r--r--drivers/thermal/hisi_thermal.c421
-rw-r--r--drivers/thermal/imx_thermal.c3
-rw-r--r--drivers/thermal/int340x_thermal/processor_thermal_device.c59
-rw-r--r--drivers/thermal/intel_powerclamp.c1
-rw-r--r--drivers/thermal/intel_quark_dts_thermal.c473
-rw-r--r--drivers/thermal/intel_soc_dts_iosf.c478
-rw-r--r--drivers/thermal/intel_soc_dts_iosf.h62
-rw-r--r--drivers/thermal/intel_soc_dts_thermal.c430
-rw-r--r--drivers/thermal/of-thermal.c41
-rw-r--r--drivers/thermal/power_allocator.c539
-rw-r--r--drivers/thermal/qcom-spmi-temp-alarm.c309
-rw-r--r--drivers/thermal/samsung/exynos_tmu.c187
-rw-r--r--drivers/thermal/samsung/exynos_tmu.h1
-rw-r--r--drivers/thermal/thermal_core.c314
-rw-r--r--drivers/thermal/thermal_core.h11
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c104
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-thermal-common.c5
-rw-r--r--drivers/thermal/x86_pkg_temp_thermal.c2
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/sh-sci.c40
707 files changed, 38920 insertions, 11721 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index d24fa19..6d4e44e 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -800,7 +800,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
result =
thermal_zone_bind_cooling_device
(thermal, trip, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
else
result =
thermal_zone_unbind_cooling_device
@@ -824,7 +825,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
if (bind)
result = thermal_zone_bind_cooling_device
(thermal, trip, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
else
result = thermal_zone_unbind_cooling_device
(thermal, trip, cdev);
@@ -841,7 +843,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
result = thermal_zone_bind_cooling_device
(thermal, THERMAL_TRIPS_NONE,
cdev, THERMAL_NO_LIMIT,
- THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
else
result = thermal_zone_unbind_cooling_device
(thermal, THERMAL_TRIPS_NONE,
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 9dca4b9..b11470a 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -98,6 +98,15 @@ config SATA_AHCI_PLATFORM
If unsure, say N.
+config AHCI_BRCMSTB
+ tristate "Broadcom STB AHCI SATA support"
+ depends on ARCH_BRCMSTB
+ help
+ This option enables support for the AHCI SATA3 controller found on
+ STB SoC's.
+
+ If unsure, say N.
+
config AHCI_DA850
tristate "DaVinci DA850 AHCI SATA support"
depends on ARCH_DAVINCI_DA850
@@ -124,6 +133,15 @@ config AHCI_IMX
If unsure, say N.
+config AHCI_CEVA
+ tristate "CEVA AHCI SATA support"
+ depends on OF
+ help
+ This option enables support for the CEVA AHCI SATA.
+ It can be found on the Xilinx Zynq UltraScale+ MPSoC.
+
+ If unsure, say N.
+
config AHCI_MVEBU
tristate "Marvell EBU AHCI SATA support"
depends on ARCH_MVEBU
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 40f7865..af70919 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -10,6 +10,8 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
+obj-$(CONFIG_AHCI_BRCMSTB) += ahci_brcmstb.o libahci.o libahci_platform.o
+obj-$(CONFIG_AHCI_CEVA) += ahci_ceva.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o
diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
index 12489ce..ed6a30c 100644
--- a/drivers/ata/acard-ahci.c
+++ b/drivers/ata/acard-ahci.c
@@ -433,6 +433,8 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
+
+ hpriv->irq = pdev->irq;
hpriv->flags |= (unsigned long)pi.private_data;
if (!(hpriv->flags & AHCI_HFLAG_NO_MSI))
@@ -498,7 +500,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
acard_ahci_pci_print_info(host);
pci_set_master(pdev);
- return ahci_host_activate(host, pdev->irq, &acard_ahci_sht);
+ return ahci_host_activate(host, &acard_ahci_sht);
}
module_pci_driver(acard_ahci_pci_driver);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 65ee944..7e62751 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -42,6 +42,7 @@
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
+#include <linux/msi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
@@ -52,6 +53,7 @@
enum {
AHCI_PCI_BAR_STA2X11 = 0,
+ AHCI_PCI_BAR_CAVIUM = 0,
AHCI_PCI_BAR_ENMOTUS = 2,
AHCI_PCI_BAR_STANDARD = 5,
};
@@ -1288,17 +1290,60 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
{}
#endif
-static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
- struct ahci_host_priv *hpriv)
+/*
+ * ahci_init_msix() only implements single MSI-X support, not multiple
+ * MSI-X per-port interrupts. This is needed for host controllers that only
+ * have MSI-X support implemented, but no MSI or intx.
+ */
+static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
+ struct ahci_host_priv *hpriv)
{
int rc, nvec;
+ struct msix_entry entry = {};
+ /* Do not init MSI-X if MSI is disabled for the device */
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
- goto intx;
+ return -ENODEV;
+
+ nvec = pci_msix_vec_count(pdev);
+ if (nvec < 0)
+ return nvec;
+
+ if (!nvec) {
+ rc = -ENODEV;
+ goto fail;
+ }
+
+ /*
+ * There can be more than one vector (e.g. for error detection or
+ * hdd hotplug). Only the first vector (entry.entry = 0) is used.
+ */
+ rc = pci_enable_msix_exact(pdev, &entry, 1);
+ if (rc < 0)
+ goto fail;
+
+ hpriv->irq = entry.vector;
+
+ return 1;
+fail:
+ dev_err(&pdev->dev,
+ "failed to enable MSI-X with error %d, # of vectors: %d\n",
+ rc, nvec);
+
+ return rc;
+}
+
+static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
+ struct ahci_host_priv *hpriv)
+{
+ int rc, nvec;
+
+ if (hpriv->flags & AHCI_HFLAG_NO_MSI)
+ return -ENODEV;
nvec = pci_msi_vec_count(pdev);
if (nvec < 0)
- goto intx;
+ return nvec;
/*
* If number of MSIs is less than number of ports then Sharing Last
@@ -1311,8 +1356,8 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
rc = pci_enable_msi_exact(pdev, nvec);
if (rc == -ENOSPC)
goto single_msi;
- else if (rc < 0)
- goto intx;
+ if (rc < 0)
+ return rc;
/* fallback to single MSI mode if the controller enforced MRSM mode */
if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
@@ -1324,15 +1369,42 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
if (nvec > 1)
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
- return nvec;
+ goto out;
single_msi:
- if (pci_enable_msi(pdev))
- goto intx;
- return 1;
+ nvec = 1;
+
+ rc = pci_enable_msi(pdev);
+ if (rc < 0)
+ return rc;
+out:
+ hpriv->irq = pdev->irq;
+
+ return nvec;
+}
+
+static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
+ struct ahci_host_priv *hpriv)
+{
+ int nvec;
+
+ nvec = ahci_init_msi(pdev, n_ports, hpriv);
+ if (nvec >= 0)
+ return nvec;
+
+ /*
+ * Currently, MSI-X support only implements single IRQ mode and
+ * exists for controllers which can't do other types of IRQ. Only
+ * set it up if MSI fails.
+ */
+ nvec = ahci_init_msix(pdev, n_ports, hpriv);
+ if (nvec >= 0)
+ return nvec;
-intx:
+ /* lagacy intx interrupts */
pci_intx(pdev, 1);
+ hpriv->irq = pdev->irq;
+
return 0;
}
@@ -1371,11 +1443,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_info(&pdev->dev,
"PDC42819 can only drive SATA devices with this driver\n");
- /* Both Connext and Enmotus devices use non-standard BARs */
+ /* Some devices use non-standard BARs */
if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06)
ahci_pci_bar = AHCI_PCI_BAR_STA2X11;
else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000)
ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS;
+ else if (pdev->vendor == 0x177d && pdev->device == 0xa01c)
+ ahci_pci_bar = AHCI_PCI_BAR_CAVIUM;
/*
* The JMicron chip 361/363 contains one SATA controller and one
@@ -1497,13 +1571,13 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
*/
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
- ahci_init_interrupts(pdev, n_ports, hpriv);
-
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
if (!host)
return -ENOMEM;
host->private_data = hpriv;
+ ahci_init_interrupts(pdev, n_ports, hpriv);
+
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
host->flags |= ATA_HOST_PARALLEL_SCAN;
else
@@ -1549,7 +1623,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);
- return ahci_host_activate(host, pdev->irq, &ahci_sht);
+ return ahci_host_activate(host, &ahci_sht);
}
module_pci_driver(ahci_pci_driver);
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 71262e0..5b8e8a0 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -238,6 +238,8 @@ enum {
AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
+ AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as
+ Edge Triggered */
/* ap->flags bits */
@@ -341,6 +343,7 @@ struct ahci_host_priv {
struct phy **phys;
unsigned nports; /* Number of ports */
void *plat_data; /* Other platform data */
+ unsigned int irq; /* interrupt line */
/*
* Optional ahci_start_engine override, if not set this gets set to the
* default ahci_start_engine during ahci_save_initial_config, this can
@@ -393,8 +396,7 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
struct ata_port_info *pi);
int ahci_reset_em(struct ata_host *host);
void ahci_print_info(struct ata_host *host, const char *scc_s);
-int ahci_host_activate(struct ata_host *host, int irq,
- struct scsi_host_template *sht);
+int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht);
void ahci_error_handler(struct ata_port *ap);
static inline void __iomem *__ahci_port_base(struct ata_host *host,
diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c
new file mode 100644
index 0000000..ce1e3a8
--- /dev/null
+++ b/drivers/ata/ahci_brcmstb.c
@@ -0,0 +1,322 @@
+/*
+ * Broadcom SATA3 AHCI Controller Driver
+ *
+ * Copyright © 2009-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ahci_platform.h>
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+
+#include "ahci.h"
+
+#define DRV_NAME "brcm-ahci"
+
+#define SATA_TOP_CTRL_VERSION 0x0
+#define SATA_TOP_CTRL_BUS_CTRL 0x4
+ #define MMIO_ENDIAN_SHIFT 0 /* CPU->AHCI */
+ #define DMADESC_ENDIAN_SHIFT 2 /* AHCI->DDR */
+ #define DMADATA_ENDIAN_SHIFT 4 /* AHCI->DDR */
+ #define PIODATA_ENDIAN_SHIFT 6
+ #define ENDIAN_SWAP_NONE 0
+ #define ENDIAN_SWAP_FULL 2
+ #define OVERRIDE_HWINIT BIT(16)
+#define SATA_TOP_CTRL_TP_CTRL 0x8
+#define SATA_TOP_CTRL_PHY_CTRL 0xc
+ #define SATA_TOP_CTRL_PHY_CTRL_1 0x0
+ #define SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE BIT(14)
+ #define SATA_TOP_CTRL_PHY_CTRL_2 0x4
+ #define SATA_TOP_CTRL_2_SW_RST_MDIOREG BIT(0)
+ #define SATA_TOP_CTRL_2_SW_RST_OOB BIT(1)
+ #define SATA_TOP_CTRL_2_SW_RST_RX BIT(2)
+ #define SATA_TOP_CTRL_2_SW_RST_TX BIT(3)
+ #define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET BIT(14)
+ #define SATA_TOP_CTRL_PHY_OFFS 0x8
+ #define SATA_TOP_MAX_PHYS 2
+#define SATA_TOP_CTRL_SATA_TP_OUT 0x1c
+#define SATA_TOP_CTRL_CLIENT_INIT_CTRL 0x20
+
+/* On big-endian MIPS, buses are reversed to big endian, so switch them back */
+#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
+#define DATA_ENDIAN 2 /* AHCI->DDR inbound accesses */
+#define MMIO_ENDIAN 2 /* CPU->AHCI outbound accesses */
+#else
+#define DATA_ENDIAN 0
+#define MMIO_ENDIAN 0
+#endif
+
+#define BUS_CTRL_ENDIAN_CONF \
+ ((DATA_ENDIAN << DMADATA_ENDIAN_SHIFT) | \
+ (DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) | \
+ (MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
+
+struct brcm_ahci_priv {
+ struct device *dev;
+ void __iomem *top_ctrl;
+ u32 port_mask;
+};
+
+static const struct ata_port_info ahci_brcm_port_info = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_platform_ops,
+};
+
+static inline u32 brcm_sata_readreg(void __iomem *addr)
+{
+ /*
+ * MIPS endianness is configured by boot strap, which also reverses all
+ * bus endianness (i.e., big-endian CPU + big endian bus ==> native
+ * endian I/O).
+ *
+ * Other architectures (e.g., ARM) either do not support big endian, or
+ * else leave I/O in little endian mode.
+ */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ return __raw_readl(addr);
+ else
+ return readl_relaxed(addr);
+}
+
+static inline void brcm_sata_writereg(u32 val, void __iomem *addr)
+{
+ /* See brcm_sata_readreg() comments */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ __raw_writel(val, addr);
+ else
+ writel_relaxed(val, addr);
+}
+
+static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
+{
+ void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
+ (port * SATA_TOP_CTRL_PHY_OFFS);
+ void __iomem *p;
+ u32 reg;
+
+ /* clear PHY_DEFAULT_POWER_STATE */
+ p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
+ reg = brcm_sata_readreg(p);
+ reg &= ~SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
+ brcm_sata_writereg(reg, p);
+
+ /* reset the PHY digital logic */
+ p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
+ reg = brcm_sata_readreg(p);
+ reg &= ~(SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
+ SATA_TOP_CTRL_2_SW_RST_RX);
+ reg |= SATA_TOP_CTRL_2_SW_RST_TX;
+ brcm_sata_writereg(reg, p);
+ reg = brcm_sata_readreg(p);
+ reg |= SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
+ brcm_sata_writereg(reg, p);
+ reg = brcm_sata_readreg(p);
+ reg &= ~SATA_TOP_CTRL_2_PHY_GLOBAL_RESET;
+ brcm_sata_writereg(reg, p);
+ (void)brcm_sata_readreg(p);
+}
+
+static void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port)
+{
+ void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
+ (port * SATA_TOP_CTRL_PHY_OFFS);
+ void __iomem *p;
+ u32 reg;
+
+ /* power-off the PHY digital logic */
+ p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
+ reg = brcm_sata_readreg(p);
+ reg |= (SATA_TOP_CTRL_2_SW_RST_MDIOREG | SATA_TOP_CTRL_2_SW_RST_OOB |
+ SATA_TOP_CTRL_2_SW_RST_RX | SATA_TOP_CTRL_2_SW_RST_TX |
+ SATA_TOP_CTRL_2_PHY_GLOBAL_RESET);
+ brcm_sata_writereg(reg, p);
+
+ /* set PHY_DEFAULT_POWER_STATE */
+ p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
+ reg = brcm_sata_readreg(p);
+ reg |= SATA_TOP_CTRL_1_PHY_DEFAULT_POWER_STATE;
+ brcm_sata_writereg(reg, p);
+}
+
+static void brcm_sata_phys_enable(struct brcm_ahci_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
+ if (priv->port_mask & BIT(i))
+ brcm_sata_phy_enable(priv, i);
+}
+
+static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < SATA_TOP_MAX_PHYS; i++)
+ if (priv->port_mask & BIT(i))
+ brcm_sata_phy_disable(priv, i);
+}
+
+static u32 brcm_ahci_get_portmask(struct platform_device *pdev,
+ struct brcm_ahci_priv *priv)
+{
+ void __iomem *ahci;
+ struct resource *res;
+ u32 impl;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci");
+ ahci = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ahci))
+ return 0;
+
+ impl = readl(ahci + HOST_PORTS_IMPL);
+
+ if (fls(impl) > SATA_TOP_MAX_PHYS)
+ dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n",
+ impl);
+ else if (!impl)
+ dev_info(priv->dev, "no ports found\n");
+
+ devm_iounmap(&pdev->dev, ahci);
+ devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+
+ return impl;
+}
+
+static void brcm_sata_init(struct brcm_ahci_priv *priv)
+{
+ /* Configure endianness */
+ brcm_sata_writereg(BUS_CTRL_ENDIAN_CONF,
+ priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL);
+}
+
+static int brcm_ahci_suspend(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ struct brcm_ahci_priv *priv = hpriv->plat_data;
+ int ret;
+
+ ret = ahci_platform_suspend(dev);
+ brcm_sata_phys_disable(priv);
+ return ret;
+}
+
+static int brcm_ahci_resume(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ struct brcm_ahci_priv *priv = hpriv->plat_data;
+
+ brcm_sata_init(priv);
+ brcm_sata_phys_enable(priv);
+ return ahci_platform_resume(dev);
+}
+
+static struct scsi_host_template ahci_platform_sht = {
+ AHCI_SHT(DRV_NAME),
+};
+
+static int brcm_ahci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct brcm_ahci_priv *priv;
+ struct ahci_host_priv *hpriv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "top-ctrl");
+ priv->top_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->top_ctrl))
+ return PTR_ERR(priv->top_ctrl);
+
+ brcm_sata_init(priv);
+
+ priv->port_mask = brcm_ahci_get_portmask(pdev, priv);
+ if (!priv->port_mask)
+ return -ENODEV;
+
+ brcm_sata_phys_enable(priv);
+
+ hpriv = ahci_platform_get_resources(pdev);
+ if (IS_ERR(hpriv))
+ return PTR_ERR(hpriv);
+ hpriv->plat_data = priv;
+
+ ret = ahci_platform_enable_resources(hpriv);
+ if (ret)
+ return ret;
+
+ ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
+ &ahci_platform_sht);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "Broadcom AHCI SATA3 registered\n");
+
+ return 0;
+}
+
+static int brcm_ahci_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ struct brcm_ahci_priv *priv = hpriv->plat_data;
+ int ret;
+
+ ret = ata_platform_remove_one(pdev);
+ if (ret)
+ return ret;
+
+ brcm_sata_phys_disable(priv);
+
+ return 0;
+}
+
+static const struct of_device_id ahci_of_match[] = {
+ {.compatible = "brcm,bcm7445-ahci"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ahci_of_match);
+
+static SIMPLE_DEV_PM_OPS(ahci_brcm_pm_ops, brcm_ahci_suspend, brcm_ahci_resume);
+
+static struct platform_driver brcm_ahci_driver = {
+ .probe = brcm_ahci_probe,
+ .remove = brcm_ahci_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = ahci_of_match,
+ .pm = &ahci_brcm_pm_ops,
+ },
+};
+module_platform_driver(brcm_ahci_driver);
+
+MODULE_DESCRIPTION("Broadcom SATA3 AHCI Controller Driver");
+MODULE_AUTHOR("Brian Norris");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sata-brcmstb");
diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
new file mode 100644
index 0000000..207649d
--- /dev/null
+++ b/drivers/ata/ahci_ceva.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015 Xilinx, Inc.
+ * CEVA AHCI SATA platform driver
+ *
+ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ahci_platform.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "ahci.h"
+
+/* Vendor Specific Register Offsets */
+#define AHCI_VEND_PCFG 0xA4
+#define AHCI_VEND_PPCFG 0xA8
+#define AHCI_VEND_PP2C 0xAC
+#define AHCI_VEND_PP3C 0xB0
+#define AHCI_VEND_PP4C 0xB4
+#define AHCI_VEND_PP5C 0xB8
+#define AHCI_VEND_PAXIC 0xC0
+#define AHCI_VEND_PTC 0xC8
+
+/* Vendor Specific Register bit definitions */
+#define PAXIC_ADBW_BW64 0x1
+#define PAXIC_MAWIDD (1 << 8)
+#define PAXIC_MARIDD (1 << 16)
+#define PAXIC_OTL (0x4 << 20)
+
+#define PCFG_TPSS_VAL (0x32 << 16)
+#define PCFG_TPRS_VAL (0x2 << 12)
+#define PCFG_PAD_VAL 0x2
+
+#define PPCFG_TTA 0x1FFFE
+#define PPCFG_PSSO_EN (1 << 28)
+#define PPCFG_PSS_EN (1 << 29)
+#define PPCFG_ESDF_EN (1 << 31)
+
+#define PP2C_CIBGMN 0x0F
+#define PP2C_CIBGMX (0x25 << 8)
+#define PP2C_CIBGN (0x18 << 16)
+#define PP2C_CINMP (0x29 << 24)
+
+#define PP3C_CWBGMN 0x04
+#define PP3C_CWBGMX (0x0B << 8)
+#define PP3C_CWBGN (0x08 << 16)
+#define PP3C_CWNMP (0x0F << 24)
+
+#define PP4C_BMX 0x0a
+#define PP4C_BNM (0x08 << 8)
+#define PP4C_SFD (0x4a << 16)
+#define PP4C_PTST (0x06 << 24)
+
+#define PP5C_RIT 0x60216
+#define PP5C_RCT (0x7f0 << 20)
+
+#define PTC_RX_WM_VAL 0x40
+#define PTC_RSVD (1 << 27)
+
+#define PORT0_BASE 0x100
+#define PORT1_BASE 0x180
+
+/* Port Control Register Bit Definitions */
+#define PORT_SCTL_SPD_GEN2 (0x2 << 4)
+#define PORT_SCTL_SPD_GEN1 (0x1 << 4)
+#define PORT_SCTL_IPM (0x3 << 8)
+
+#define PORT_BASE 0x100
+#define PORT_OFFSET 0x80
+#define NR_PORTS 2
+#define DRV_NAME "ahci-ceva"
+#define CEVA_FLAG_BROKEN_GEN2 1
+
+struct ceva_ahci_priv {
+ struct platform_device *ahci_pdev;
+ int flags;
+};
+
+static struct ata_port_operations ahci_ceva_ops = {
+ .inherits = &ahci_platform_ops,
+};
+
+static const struct ata_port_info ahci_ceva_port_info = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ceva_ops,
+};
+
+static void ahci_ceva_setup(struct ahci_host_priv *hpriv)
+{
+ void __iomem *mmio = hpriv->mmio;
+ struct ceva_ahci_priv *cevapriv = hpriv->plat_data;
+ u32 tmp;
+ int i;
+
+ /*
+ * AXI Data bus width to 64
+ * Set Mem Addr Read, Write ID for data transfers
+ * Transfer limit to 72 DWord
+ */
+ tmp = PAXIC_ADBW_BW64 | PAXIC_MAWIDD | PAXIC_MARIDD | PAXIC_OTL;
+ writel(tmp, mmio + AHCI_VEND_PAXIC);
+
+ /* Set AHCI Enable */
+ tmp = readl(mmio + HOST_CTL);
+ tmp |= HOST_AHCI_EN;
+ writel(tmp, mmio + HOST_CTL);
+
+ for (i = 0; i < NR_PORTS; i++) {
+ /* TPSS TPRS scalars, CISE and Port Addr */
+ tmp = PCFG_TPSS_VAL | PCFG_TPRS_VAL | (PCFG_PAD_VAL + i);
+ writel(tmp, mmio + AHCI_VEND_PCFG);
+
+ /* Port Phy Cfg register enables */
+ tmp = PPCFG_TTA | PPCFG_PSS_EN | PPCFG_ESDF_EN;
+ writel(tmp, mmio + AHCI_VEND_PPCFG);
+
+ /* Phy Control OOB timing parameters COMINIT */
+ tmp = PP2C_CIBGMN | PP2C_CIBGMX | PP2C_CIBGN | PP2C_CINMP;
+ writel(tmp, mmio + AHCI_VEND_PP2C);
+
+ /* Phy Control OOB timing parameters COMWAKE */
+ tmp = PP3C_CWBGMN | PP3C_CWBGMX | PP3C_CWBGN | PP3C_CWNMP;
+ writel(tmp, mmio + AHCI_VEND_PP3C);
+
+ /* Phy Control Burst timing setting */
+ tmp = PP4C_BMX | PP4C_BNM | PP4C_SFD | PP4C_PTST;
+ writel(tmp, mmio + AHCI_VEND_PP4C);
+
+ /* Rate Change Timer and Retry Interval Timer setting */
+ tmp = PP5C_RIT | PP5C_RCT;
+ writel(tmp, mmio + AHCI_VEND_PP5C);
+
+ /* Rx Watermark setting */
+ tmp = PTC_RX_WM_VAL | PTC_RSVD;
+ writel(tmp, mmio + AHCI_VEND_PTC);
+
+ /* Default to Gen 2 Speed and Gen 1 if Gen2 is broken */
+ tmp = PORT_SCTL_SPD_GEN2 | PORT_SCTL_IPM;
+ if (cevapriv->flags & CEVA_FLAG_BROKEN_GEN2)
+ tmp = PORT_SCTL_SPD_GEN1 | PORT_SCTL_IPM;
+ writel(tmp, mmio + PORT_SCR_CTL + PORT_BASE + PORT_OFFSET * i);
+ }
+}
+
+static struct scsi_host_template ahci_platform_sht = {
+ AHCI_SHT(DRV_NAME),
+};
+
+static int ceva_ahci_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct ahci_host_priv *hpriv;
+ struct ceva_ahci_priv *cevapriv;
+ int rc;
+
+ cevapriv = devm_kzalloc(dev, sizeof(*cevapriv), GFP_KERNEL);
+ if (!cevapriv)
+ return -ENOMEM;
+
+ cevapriv->ahci_pdev = pdev;
+
+ hpriv = ahci_platform_get_resources(pdev);
+ if (IS_ERR(hpriv))
+ return PTR_ERR(hpriv);
+
+ rc = ahci_platform_enable_resources(hpriv);
+ if (rc)
+ return rc;
+
+ if (of_property_read_bool(np, "ceva,broken-gen2"))
+ cevapriv->flags = CEVA_FLAG_BROKEN_GEN2;
+
+ hpriv->plat_data = cevapriv;
+
+ /* CEVA specific initialization */
+ ahci_ceva_setup(hpriv);
+
+ rc = ahci_platform_init_host(pdev, hpriv, &ahci_ceva_port_info,
+ &ahci_platform_sht);
+ if (rc)
+ goto disable_resources;
+
+ return 0;
+
+disable_resources:
+ ahci_platform_disable_resources(hpriv);
+ return rc;
+}
+
+static int __maybe_unused ceva_ahci_suspend(struct device *dev)
+{
+ return ahci_platform_suspend_host(dev);
+}
+
+static int __maybe_unused ceva_ahci_resume(struct device *dev)
+{
+ return ahci_platform_resume_host(dev);
+}
+
+static SIMPLE_DEV_PM_OPS(ahci_ceva_pm_ops, ceva_ahci_suspend, ceva_ahci_resume);
+
+static const struct of_device_id ceva_ahci_of_match[] = {
+ { .compatible = "ceva,ahci-1v84" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ceva_ahci_of_match);
+
+static struct platform_driver ceva_ahci_driver = {
+ .probe = ceva_ahci_probe,
+ .remove = ata_platform_remove_one,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = ceva_ahci_of_match,
+ .pm = &ahci_ceva_pm_ops,
+ },
+};
+module_platform_driver(ceva_ahci_driver);
+
+MODULE_DESCRIPTION("CEVA AHCI SATA platform driver");
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index 5928d07..8490d37 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -62,6 +62,26 @@ static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv)
writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
}
+static int ahci_mvebu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return ahci_platform_suspend_host(&pdev->dev);
+}
+
+static int ahci_mvebu_resume(struct platform_device *pdev)
+{
+ struct ata_host *host = platform_get_drvdata(pdev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ const struct mbus_dram_target_info *dram;
+
+ dram = mv_mbus_dram_info();
+ if (dram)
+ ahci_mvebu_mbus_config(hpriv, dram);
+
+ ahci_mvebu_regret_option(hpriv);
+
+ return ahci_platform_resume_host(&pdev->dev);
+}
+
static const struct ata_port_info ahci_mvebu_port_info = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
@@ -120,6 +140,8 @@ MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match);
static struct platform_driver ahci_mvebu_driver = {
.probe = ahci_mvebu_probe,
.remove = ata_platform_remove_one,
+ .suspend = ahci_mvebu_suspend,
+ .resume = ahci_mvebu_resume,
.driver = {
.name = DRV_NAME,
.of_match_table = ahci_mvebu_of_match,
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 78d6ae0..614c78f 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -74,6 +74,7 @@ static const struct of_device_id ahci_of_match[] = {
{ .compatible = "ibm,476gtr-ahci", },
{ .compatible = "snps,dwc-ahci", },
{ .compatible = "hisilicon,hisi-ahci", },
+ { .compatible = "fsl,qoriq-ahci", },
{},
};
MODULE_DEVICE_TABLE(of, ahci_of_match);
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
index 2b78510..e2c6d9e 100644
--- a/drivers/ata/ahci_xgene.c
+++ b/drivers/ata/ahci_xgene.c
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/ahci_platform.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/phy/phy.h>
#include "ahci.h"
@@ -84,6 +85,11 @@
/* Max retry for link down */
#define MAX_LINK_DOWN_RETRY 3
+enum xgene_ahci_version {
+ XGENE_AHCI_V1 = 1,
+ XGENE_AHCI_V2,
+};
+
struct xgene_ahci_context {
struct ahci_host_priv *hpriv;
struct device *dev;
@@ -542,7 +548,7 @@ softreset_retry:
return rc;
}
-static struct ata_port_operations xgene_ahci_ops = {
+static struct ata_port_operations xgene_ahci_v1_ops = {
.inherits = &ahci_ops,
.host_stop = xgene_ahci_host_stop,
.hardreset = xgene_ahci_hardreset,
@@ -552,11 +558,25 @@ static struct ata_port_operations xgene_ahci_ops = {
.pmp_softreset = xgene_ahci_pmp_softreset
};
-static const struct ata_port_info xgene_ahci_port_info = {
+static const struct ata_port_info xgene_ahci_v1_port_info = {
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &xgene_ahci_v1_ops,
+};
+
+static struct ata_port_operations xgene_ahci_v2_ops = {
+ .inherits = &ahci_ops,
+ .host_stop = xgene_ahci_host_stop,
+ .hardreset = xgene_ahci_hardreset,
+ .read_id = xgene_ahci_read_id,
+};
+
+static const struct ata_port_info xgene_ahci_v2_port_info = {
.flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &xgene_ahci_ops,
+ .port_ops = &xgene_ahci_v2_ops,
};
static int xgene_ahci_hw_init(struct ahci_host_priv *hpriv)
@@ -629,12 +649,32 @@ static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_ahci_acpi_match[] = {
+ { "APMC0D0D", XGENE_AHCI_V1},
+ { "APMC0D32", XGENE_AHCI_V2},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match);
+#endif
+
+static const struct of_device_id xgene_ahci_of_match[] = {
+ {.compatible = "apm,xgene-ahci", .data = (void *) XGENE_AHCI_V1},
+ {.compatible = "apm,xgene-ahci-v2", .data = (void *) XGENE_AHCI_V2},
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_ahci_of_match);
+
static int xgene_ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
struct xgene_ahci_context *ctx;
struct resource *res;
+ const struct of_device_id *of_devid;
+ enum xgene_ahci_version version = XGENE_AHCI_V1;
+ const struct ata_port_info *ppi[] = { &xgene_ahci_v1_port_info,
+ &xgene_ahci_v2_port_info };
int rc;
hpriv = ahci_platform_get_resources(pdev);
@@ -677,6 +717,35 @@ static int xgene_ahci_probe(struct platform_device *pdev)
ctx->csr_mux = csr;
}
+ of_devid = of_match_device(xgene_ahci_of_match, dev);
+ if (of_devid) {
+ if (of_devid->data)
+ version = (enum xgene_ahci_version) of_devid->data;
+ }
+#ifdef CONFIG_ACPI
+ else {
+ const struct acpi_device_id *acpi_id;
+ struct acpi_device_info *info;
+ acpi_status status;
+
+ acpi_id = acpi_match_device(xgene_ahci_acpi_match, &pdev->dev);
+ if (!acpi_id) {
+ dev_warn(&pdev->dev, "No node entry in ACPI table. Assume version1\n");
+ version = XGENE_AHCI_V1;
+ } else if (acpi_id->driver_data) {
+ version = (enum xgene_ahci_version) acpi_id->driver_data;
+ status = acpi_get_object_info(ACPI_HANDLE(&pdev->dev), &info);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(&pdev->dev, "%s: Error reading device info. Assume version1\n",
+ __func__);
+ version = XGENE_AHCI_V1;
+ }
+ if (info->valid & ACPI_VALID_CID)
+ version = XGENE_AHCI_V2;
+ }
+ }
+#endif
+
dev_dbg(dev, "VAddr 0x%p Mmio VAddr 0x%p\n", ctx->csr_core,
hpriv->mmio);
@@ -704,9 +773,19 @@ static int xgene_ahci_probe(struct platform_device *pdev)
/* Configure the host controller */
xgene_ahci_hw_init(hpriv);
skip_clk_phy:
- hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ;
- rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info,
+ switch (version) {
+ case XGENE_AHCI_V1:
+ hpriv->flags = AHCI_HFLAG_NO_NCQ;
+ break;
+ case XGENE_AHCI_V2:
+ hpriv->flags |= AHCI_HFLAG_YES_FBS | AHCI_HFLAG_EDGE_IRQ;
+ break;
+ default:
+ break;
+ }
+
+ rc = ahci_platform_init_host(pdev, hpriv, ppi[version - 1],
&ahci_platform_sht);
if (rc)
goto disable_resources;
@@ -719,20 +798,6 @@ disable_resources:
return rc;
}
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id xgene_ahci_acpi_match[] = {
- { "APMC0D0D", },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match);
-#endif
-
-static const struct of_device_id xgene_ahci_of_match[] = {
- {.compatible = "apm,xgene-ahci"},
- {},
-};
-MODULE_DEVICE_TABLE(of, xgene_ahci_of_match);
-
static struct platform_driver xgene_ahci_driver = {
.probe = xgene_ahci_probe,
.remove = ata_platform_remove_one,
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 287c4ba..d256a66 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1825,11 +1825,38 @@ static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
return IRQ_WAKE_THREAD;
}
-static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
+static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
+{
+ unsigned int i, handled = 0;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap;
+
+ if (!(irq_masked & (1 << i)))
+ continue;
+
+ ap = host->ports[i];
+ if (ap) {
+ ahci_port_intr(ap);
+ VPRINTK("port %u\n", i);
+ } else {
+ VPRINTK("port %u (no irq)\n", i);
+ if (ata_ratelimit())
+ dev_warn(host->dev,
+ "interrupt on disabled port %u\n", i);
+ }
+
+ handled = 1;
+ }
+
+ return handled;
+}
+
+static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv;
- unsigned int i, handled = 0;
+ unsigned int rc = 0;
void __iomem *mmio;
u32 irq_stat, irq_masked;
@@ -1847,25 +1874,44 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
spin_lock(&host->lock);
- for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap;
+ /*
+ * HOST_IRQ_STAT behaves as edge triggered latch meaning that
+ * it should be cleared before all the port events are cleared.
+ */
+ writel(irq_stat, mmio + HOST_IRQ_STAT);
- if (!(irq_masked & (1 << i)))
- continue;
+ rc = ahci_handle_port_intr(host, irq_masked);
- ap = host->ports[i];
- if (ap) {
- ahci_port_intr(ap);
- VPRINTK("port %u\n", i);
- } else {
- VPRINTK("port %u (no irq)\n", i);
- if (ata_ratelimit())
- dev_warn(host->dev,
- "interrupt on disabled port %u\n", i);
- }
+ spin_unlock(&host->lock);
- handled = 1;
- }
+ VPRINTK("EXIT\n");
+
+ return IRQ_RETVAL(rc);
+}
+
+static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct ahci_host_priv *hpriv;
+ unsigned int rc = 0;
+ void __iomem *mmio;
+ u32 irq_stat, irq_masked;
+
+ VPRINTK("ENTER\n");
+
+ hpriv = host->private_data;
+ mmio = hpriv->mmio;
+
+ /* sigh. 0xffffffff is a valid return from h/w */
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ irq_masked = irq_stat & hpriv->port_map;
+
+ spin_lock(&host->lock);
+
+ rc = ahci_handle_port_intr(host, irq_masked);
/* HOST_IRQ_STAT behaves as level triggered latch meaning that
* it should be cleared after all the port events are cleared;
@@ -1882,7 +1928,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
VPRINTK("EXIT\n");
- return IRQ_RETVAL(handled);
+ return IRQ_RETVAL(rc);
}
unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
@@ -2297,7 +2343,7 @@ static int ahci_port_start(struct ata_port *ap)
/*
* Switch to per-port locking in case each port has its own MSI vector.
*/
- if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
+ if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
spin_lock_init(&pp->lock);
ap->lock = &pp->lock;
}
@@ -2425,7 +2471,10 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
rc = ata_host_start(host);
if (rc)
return rc;
-
+ /*
+ * Requests IRQs according to AHCI-1.1 when multiple MSIs were
+ * allocated. That is one MSI per port, starting from @irq.
+ */
for (i = 0; i < host->n_ports; i++) {
struct ahci_port_priv *pp = host->ports[i]->private_data;
@@ -2464,29 +2513,27 @@ out_free_irqs:
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
- * @irq: base IRQ number to request
* @sht: scsi_host_template to use when registering the host
*
- * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
- * when multiple MSIs were allocated. That is one MSI per port, starting
- * from @irq.
- *
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int ahci_host_activate(struct ata_host *host, int irq,
- struct scsi_host_template *sht)
+int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
{
struct ahci_host_priv *hpriv = host->private_data;
+ int irq = hpriv->irq;
int rc;
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
+ else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
+ rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
+ IRQF_SHARED, sht);
else
- rc = ata_host_activate(host, irq, ahci_single_irq_intr,
+ rc = ata_host_activate(host, irq, ahci_single_level_irq_intr,
IRQF_SHARED, sht);
return rc;
}
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index d89305d..aaa761b 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -518,6 +518,8 @@ int ahci_platform_init_host(struct platform_device *pdev,
return -EINVAL;
}
+ hpriv->irq = irq;
+
/* prepare host */
pi.private_data = (void *)(unsigned long)hpriv->flags;
@@ -588,7 +590,7 @@ int ahci_platform_init_host(struct platform_device *pdev,
ahci_init_controller(host);
ahci_print_info(host, "platform");
- return ahci_host_activate(host, irq, sht);
+ return ahci_host_activate(host, sht);
}
EXPORT_SYMBOL_GPL(ahci_platform_init_host);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 577849c..e83fc3d 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -3654,7 +3654,7 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params,
* EH context.
*
* RETURNS:
- * 0 on succes, -errno otherwise.
+ * 0 on success, -errno otherwise.
*/
int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
bool spm_wakeup)
@@ -4225,7 +4225,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
/* devices that don't properly handle queued TRIM commands */
- { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
@@ -6456,12 +6456,7 @@ static int __init ata_parse_force_one(char **cur,
struct ata_force_ent *force_ent,
const char **reason)
{
- /* FIXME: Currently, there's no way to tag init const data and
- * using __initdata causes build failure on some versions of
- * gcc. Once __initdataconst is implemented, add const to the
- * following structure.
- */
- static struct ata_force_param force_tbl[] __initdata = {
+ static const struct ata_force_param force_tbl[] __initconst = {
{ "40c", .cbl = ATA_CBL_PATA40 },
{ "80c", .cbl = ATA_CBL_PATA80 },
{ "short40c", .cbl = ATA_CBL_PATA40_SHORT },
@@ -6472,6 +6467,8 @@ static int __init ata_parse_force_one(char **cur,
{ "3.0Gbps", .spd_limit = 2 },
{ "noncq", .horkage_on = ATA_HORKAGE_NONCQ },
{ "ncq", .horkage_off = ATA_HORKAGE_NONCQ },
+ { "noncqtrim", .horkage_on = ATA_HORKAGE_NO_NCQ_TRIM },
+ { "ncqtrim", .horkage_off = ATA_HORKAGE_NO_NCQ_TRIM },
{ "dump_id", .horkage_on = ATA_HORKAGE_DUMP_ID },
{ "pio0", .xfer_mask = 1 << (ATA_SHIFT_PIO + 0) },
{ "pio1", .xfer_mask = 1 << (ATA_SHIFT_PIO + 1) },
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index cf0022e..7465031 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1507,16 +1507,21 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
{
struct ata_taskfile tf;
unsigned int err_mask;
+ bool dma = false;
DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page);
+retry:
ata_tf_init(dev, &tf);
- if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id)) {
+ if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id) &&
+ !(dev->horkage & ATA_HORKAGE_NO_NCQ_LOG)) {
tf.command = ATA_CMD_READ_LOG_DMA_EXT;
tf.protocol = ATA_PROT_DMA;
+ dma = true;
} else {
tf.command = ATA_CMD_READ_LOG_EXT;
tf.protocol = ATA_PROT_PIO;
+ dma = false;
}
tf.lbal = log;
tf.lbam = page;
@@ -1527,6 +1532,12 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE,
buf, sectors * ATA_SECT_SIZE, 0);
+ if (err_mask && dma) {
+ dev->horkage |= ATA_HORKAGE_NO_NCQ_LOG;
+ ata_dev_warn(dev, "READ LOG DMA EXT failed, trying unqueued\n");
+ goto retry;
+ }
+
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
index 3227b7c..d6c37bc 100644
--- a/drivers/ata/libata-transport.c
+++ b/drivers/ata/libata-transport.c
@@ -560,6 +560,27 @@ show_ata_dev_gscr(struct device *dev,
static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL);
+static ssize_t
+show_ata_dev_trim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ata_device *ata_dev = transport_class_to_dev(dev);
+ unsigned char *mode;
+
+ if (!ata_id_has_trim(ata_dev->id))
+ mode = "unsupported";
+ else if (ata_dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM)
+ mode = "forced_unqueued";
+ else if (ata_fpdma_dsm_supported(ata_dev))
+ mode = "queued";
+ else
+ mode = "unqueued";
+
+ return snprintf(buf, 20, "%s\n", mode);
+}
+
+static DEVICE_ATTR(trim, S_IRUGO, show_ata_dev_trim, NULL);
+
static DECLARE_TRANSPORT_CLASS(ata_dev_class,
"ata_device", NULL, NULL, NULL);
@@ -733,6 +754,7 @@ struct scsi_transport_template *ata_attach_transport(void)
SETUP_DEV_ATTRIBUTE(ering);
SETUP_DEV_ATTRIBUTE(id);
SETUP_DEV_ATTRIBUTE(gscr);
+ SETUP_DEV_ATTRIBUTE(trim);
BUG_ON(count > ATA_DEV_ATTRS);
i->dev_attrs[count] = NULL;
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index cbc3de7..0038dc4 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -352,7 +352,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
};
const struct ata_port_info *ppi[] = { &info_hpt366, NULL };
- void *hpriv = NULL;
+ const void *hpriv = NULL;
u32 reg1;
int rc;
@@ -383,7 +383,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
break;
}
/* Now kick off ATA set up */
- return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, hpriv, 0);
+ return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, (void *)hpriv, 0);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
index fa44eb2..cbb5a47 100644
--- a/drivers/ata/pata_samsung_cf.c
+++ b/drivers/ata/pata_samsung_cf.c
@@ -638,7 +638,7 @@ static const struct dev_pm_ops pata_s3c_pm_ops = {
#endif
/* driver device registration */
-static struct platform_device_id pata_s3c_driver_ids[] = {
+static const struct platform_device_id pata_s3c_driver_ids[] = {
{
.name = "s3c64xx-pata",
.driver_data = TYPE_S3C64XX,
diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
index 24e311f..8638d57 100644
--- a/drivers/ata/sata_highbank.c
+++ b/drivers/ata/sata_highbank.c
@@ -499,6 +499,7 @@ static int ahci_highbank_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ hpriv->irq = irq;
hpriv->flags |= (unsigned long)pi.private_data;
hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
@@ -568,7 +569,7 @@ static int ahci_highbank_probe(struct platform_device *pdev)
ahci_init_controller(host);
ahci_print_info(host, "platform");
- rc = ahci_host_activate(host, irq, &ahci_highbank_platform_sht);
+ rc = ahci_host_activate(host, &ahci_highbank_platform_sht);
if (rc)
goto err0;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 7ece85f..734f563 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -599,7 +599,7 @@ MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
MODULE_VERSION(DRV_VERSION);
static bool adma_enabled;
-static bool swncq_enabled = 1;
+static bool swncq_enabled = true;
static bool msi_enabled;
static void nv_adma_register_mode(struct ata_port *ap)
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index b905e98..efd19c2 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -38,6 +38,7 @@
#include <linux/mutex.h>
#include <linux/major.h>
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/genhd.h>
#include <linux/idr.h>
#include <net/tcp.h>
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 81fde9e..a151853 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2359,7 +2359,7 @@ static void drbd_cleanup(void)
* @congested_data: User data
* @bdi_bits: Bits the BDI flusher thread is currently interested in
*
- * Returns 1<<BDI_async_congested and/or 1<<BDI_sync_congested if we are congested.
+ * Returns 1<<WB_async_congested and/or 1<<WB_sync_congested if we are congested.
*/
static int drbd_congested(void *congested_data, int bdi_bits)
{
@@ -2376,14 +2376,14 @@ static int drbd_congested(void *congested_data, int bdi_bits)
}
if (test_bit(CALLBACK_PENDING, &first_peer_device(device)->connection->flags)) {
- r |= (1 << BDI_async_congested);
+ r |= (1 << WB_async_congested);
/* Without good local data, we would need to read from remote,
* and that would need the worker thread as well, which is
* currently blocked waiting for that usermode helper to
* finish.
*/
if (!get_ldev_if_state(device, D_UP_TO_DATE))
- r |= (1 << BDI_sync_congested);
+ r |= (1 << WB_sync_congested);
else
put_ldev(device);
r &= bdi_bits;
@@ -2399,9 +2399,9 @@ static int drbd_congested(void *congested_data, int bdi_bits)
reason = 'b';
}
- if (bdi_bits & (1 << BDI_async_congested) &&
+ if (bdi_bits & (1 << WB_async_congested) &&
test_bit(NET_CONGESTED, &first_peer_device(device)->connection->flags)) {
- r |= (1 << BDI_async_congested);
+ r |= (1 << WB_async_congested);
reason = reason == 'b' ? 'a' : 'n';
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index d7173cb..40580dc 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -86,8 +86,6 @@ static DEFINE_MUTEX(loop_index_mutex);
static int max_part;
static int part_shift;
-static struct workqueue_struct *loop_wq;
-
static int transfer_xor(struct loop_device *lo, int cmd,
struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
@@ -476,6 +474,28 @@ static int loop_flush(struct loop_device *lo)
return loop_switch(lo, NULL);
}
+static void loop_reread_partitions(struct loop_device *lo,
+ struct block_device *bdev)
+{
+ int rc;
+
+ /*
+ * bd_mutex has been held already in release path, so don't
+ * acquire it if this function is called in such case.
+ *
+ * If the reread partition isn't from release path, lo_refcnt
+ * must be at least one and it can only become zero when the
+ * current holder is released.
+ */
+ if (!atomic_read(&lo->lo_refcnt))
+ rc = __blkdev_reread_part(bdev);
+ else
+ rc = blkdev_reread_part(bdev);
+ if (rc)
+ pr_warn("%s: partition scan of loop%d (%s) failed (rc=%d)\n",
+ __func__, lo->lo_number, lo->lo_file_name, rc);
+}
+
/*
* loop_change_fd switched the backing store of a loopback device to
* a new file. This is useful for operating system installers to free up
@@ -524,7 +544,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
fput(old_file);
if (lo->lo_flags & LO_FLAGS_PARTSCAN)
- ioctl_by_bdev(bdev, BLKRRPART, 0);
+ loop_reread_partitions(lo, bdev);
return 0;
out_putf:
@@ -725,6 +745,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
size = get_loop_size(lo, file);
if ((loff_t)(sector_t)size != size)
goto out_putf;
+ error = -ENOMEM;
+ lo->wq = alloc_workqueue("kloopd%d",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_UNBOUND, 16,
+ lo->lo_number);
+ if (!lo->wq)
+ goto out_putf;
error = 0;
@@ -755,7 +781,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
if (part_shift)
lo->lo_flags |= LO_FLAGS_PARTSCAN;
if (lo->lo_flags & LO_FLAGS_PARTSCAN)
- ioctl_by_bdev(bdev, BLKRRPART, 0);
+ loop_reread_partitions(lo, bdev);
/* Grab the block_device to prevent its destruction after we
* put /dev/loopXX inode. Later in loop_clr_fd() we bdput(bdev).
@@ -827,7 +853,7 @@ static int loop_clr_fd(struct loop_device *lo)
* <dev>/do something like mkfs/losetup -d <dev> causing the losetup -d
* command to fail with EBUSY.
*/
- if (lo->lo_refcnt > 1) {
+ if (atomic_read(&lo->lo_refcnt) > 1) {
lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
mutex_unlock(&lo->lo_ctl_mutex);
return 0;
@@ -836,6 +862,9 @@ static int loop_clr_fd(struct loop_device *lo)
if (filp == NULL)
return -EINVAL;
+ /* freeze request queue during the transition */
+ blk_mq_freeze_queue(lo->lo_queue);
+
spin_lock_irq(&lo->lo_lock);
lo->lo_state = Lo_rundown;
lo->lo_backing_file = NULL;
@@ -867,11 +896,15 @@ static int loop_clr_fd(struct loop_device *lo)
lo->lo_state = Lo_unbound;
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
+ blk_mq_unfreeze_queue(lo->lo_queue);
+
if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev)
- ioctl_by_bdev(bdev, BLKRRPART, 0);
+ loop_reread_partitions(lo, bdev);
lo->lo_flags = 0;
if (!part_shift)
lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
+ destroy_workqueue(lo->wq);
+ lo->wq = NULL;
mutex_unlock(&lo->lo_ctl_mutex);
/*
* Need not hold lo_ctl_mutex to fput backing file.
@@ -943,7 +976,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
!(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
lo->lo_flags |= LO_FLAGS_PARTSCAN;
lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
- ioctl_by_bdev(lo->lo_device, BLKRRPART, 0);
+ loop_reread_partitions(lo, lo->lo_device);
}
lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
@@ -1324,9 +1357,7 @@ static int lo_open(struct block_device *bdev, fmode_t mode)
goto out;
}
- mutex_lock(&lo->lo_ctl_mutex);
- lo->lo_refcnt++;
- mutex_unlock(&lo->lo_ctl_mutex);
+ atomic_inc(&lo->lo_refcnt);
out:
mutex_unlock(&loop_index_mutex);
return err;
@@ -1337,11 +1368,10 @@ static void lo_release(struct gendisk *disk, fmode_t mode)
struct loop_device *lo = disk->private_data;
int err;
- mutex_lock(&lo->lo_ctl_mutex);
-
- if (--lo->lo_refcnt)
- goto out;
+ if (atomic_dec_return(&lo->lo_refcnt))
+ return;
+ mutex_lock(&lo->lo_ctl_mutex);
if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) {
/*
* In autoclear mode, stop the loop thread
@@ -1358,7 +1388,6 @@ static void lo_release(struct gendisk *disk, fmode_t mode)
loop_flush(lo);
}
-out:
mutex_unlock(&lo->lo_ctl_mutex);
}
@@ -1425,9 +1454,13 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct loop_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
+ struct loop_device *lo = cmd->rq->q->queuedata;
blk_mq_start_request(bd->rq);
+ if (lo->lo_state != Lo_bound)
+ return -EIO;
+
if (cmd->rq->cmd_flags & REQ_WRITE) {
struct loop_device *lo = cmd->rq->q->queuedata;
bool need_sched = true;
@@ -1441,9 +1474,9 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx,
spin_unlock_irq(&lo->lo_lock);
if (need_sched)
- queue_work(loop_wq, &lo->write_work);
+ queue_work(lo->wq, &lo->write_work);
} else {
- queue_work(loop_wq, &cmd->read_work);
+ queue_work(lo->wq, &cmd->read_work);
}
return BLK_MQ_RQ_QUEUE_OK;
@@ -1455,9 +1488,6 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
struct loop_device *lo = cmd->rq->q->queuedata;
int ret = -EIO;
- if (lo->lo_state != Lo_bound)
- goto failed;
-
if (write && (lo->lo_flags & LO_FLAGS_READ_ONLY))
goto failed;
@@ -1594,6 +1624,7 @@ static int loop_add(struct loop_device **l, int i)
disk->flags |= GENHD_FL_NO_PART_SCAN;
disk->flags |= GENHD_FL_EXT_DEVT;
mutex_init(&lo->lo_ctl_mutex);
+ atomic_set(&lo->lo_refcnt, 0);
lo->lo_number = i;
spin_lock_init(&lo->lo_lock);
disk->major = LOOP_MAJOR;
@@ -1711,7 +1742,7 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd,
mutex_unlock(&lo->lo_ctl_mutex);
break;
}
- if (lo->lo_refcnt > 0) {
+ if (atomic_read(&lo->lo_refcnt) > 0) {
ret = -EBUSY;
mutex_unlock(&lo->lo_ctl_mutex);
break;
@@ -1806,13 +1837,6 @@ static int __init loop_init(void)
goto misc_out;
}
- loop_wq = alloc_workqueue("kloopd",
- WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_UNBOUND, 0);
- if (!loop_wq) {
- err = -ENOMEM;
- goto misc_out;
- }
-
blk_register_region(MKDEV(LOOP_MAJOR, 0), range,
THIS_MODULE, loop_probe, NULL, NULL);
@@ -1850,8 +1874,6 @@ static void __exit loop_exit(void)
blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range);
unregister_blkdev(LOOP_MAJOR, "loop");
- destroy_workqueue(loop_wq);
-
misc_deregister(&loop_misc);
}
diff --git a/drivers/block/loop.h b/drivers/block/loop.h
index 301c27f..25e8997 100644
--- a/drivers/block/loop.h
+++ b/drivers/block/loop.h
@@ -28,7 +28,7 @@ struct loop_func_table;
struct loop_device {
int lo_number;
- int lo_refcnt;
+ atomic_t lo_refcnt;
loff_t lo_offset;
loff_t lo_sizelimit;
int lo_flags;
@@ -54,6 +54,7 @@ struct loop_device {
gfp_t old_gfp_mask;
spinlock_t lo_lock;
+ struct workqueue_struct *wq;
struct list_head write_cmd_head;
struct work_struct write_work;
bool write_started;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 3bd7ca9..4a2ef09 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -163,12 +163,6 @@ static bool mtip_check_surprise_removal(struct pci_dev *pdev)
else
dev_warn(&dd->pdev->dev,
"%s: dd->queue is NULL\n", __func__);
- if (dd->port) {
- set_bit(MTIP_PF_SR_CLEANUP_BIT, &dd->port->flags);
- wake_up_interruptible(&dd->port->svc_wait);
- } else
- dev_warn(&dd->pdev->dev,
- "%s: dd->port is NULL\n", __func__);
return true; /* device removed */
}
@@ -269,8 +263,11 @@ static int mtip_hba_reset(struct driver_data *dd)
/* Flush */
readl(dd->mmio + HOST_CTL);
- /* Spin for up to 2 seconds, waiting for reset acknowledgement */
- timeout = jiffies + msecs_to_jiffies(2000);
+ /*
+ * Spin for up to 10 seconds waiting for reset acknowledgement. Spec
+ * is 1 sec but in LUN failure conditions, up to 10 secs are required
+ */
+ timeout = jiffies + msecs_to_jiffies(10000);
do {
mdelay(10);
if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))
@@ -623,8 +620,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
- if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) &&
- test_bit(MTIP_TAG_INTERNAL, port->allocated)) {
+ if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) {
cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL);
dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n");
@@ -896,6 +892,10 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data)
/* Acknowledge the interrupt status on the port.*/
port_stat = readl(port->mmio + PORT_IRQ_STAT);
+ if (unlikely(port_stat == 0xFFFFFFFF)) {
+ mtip_check_surprise_removal(dd->pdev);
+ return IRQ_HANDLED;
+ }
writel(port_stat, port->mmio + PORT_IRQ_STAT);
/* Demux port status */
@@ -991,15 +991,10 @@ static bool mtip_pause_ncq(struct mtip_port *port,
reply = port->rxfis + RX_FIS_D2H_REG;
task_file_data = readl(port->mmio+PORT_TFDATA);
- if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
- clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
-
if ((task_file_data & 1))
return false;
if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
- set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
- set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
port->ic_pause_timer = jiffies;
return true;
} else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1011,8 +1006,10 @@ static bool mtip_pause_ncq(struct mtip_port *port,
((fis->command == 0xFC) &&
(fis->features == 0x27 || fis->features == 0x72 ||
fis->features == 0x62 || fis->features == 0x26))) {
+ clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
/* Com reset after secure erase or lowlevel format */
mtip_restart_port(port);
+ clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
return false;
}
@@ -1112,9 +1109,10 @@ static int mtip_exec_internal_command(struct mtip_port *port,
int_cmd = mtip_get_int_command(dd);
set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
- port->ic_pause_timer = 0;
- clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ if (fis->command == ATA_CMD_SEC_ERASE_PREP)
+ set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+
clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags);
if (atomic == GFP_KERNEL) {
@@ -1251,11 +1249,11 @@ static int mtip_exec_internal_command(struct mtip_port *port,
exec_ic_exit:
/* Clear the allocated and active bits for the internal command. */
mtip_put_int_command(dd, int_cmd);
+ clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
if (rv >= 0 && mtip_pause_ncq(port, fis)) {
/* NCQ paused */
return rv;
}
- clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
wake_up_interruptible(&port->svc_wait);
return rv;
@@ -2625,18 +2623,6 @@ static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf,
readl(dd->mmio + HOST_IRQ_STAT));
size += sprintf(&buf[size], "\n");
- size += sprintf(&buf[size], "L/ Allocated : [ 0x");
-
- for (n = dd->slot_groups-1; n >= 0; n--) {
- if (sizeof(long) > sizeof(u32))
- group_allocated =
- dd->port->allocated[n/2] >> (32*(n&1));
- else
- group_allocated = dd->port->allocated[n];
- size += sprintf(&buf[size], "%08X ", group_allocated);
- }
- size += sprintf(&buf[size], "]\n");
-
size += sprintf(&buf[size], "L/ Commands in Q : [ 0x");
for (n = dd->slot_groups-1; n >= 0; n--) {
@@ -2780,48 +2766,6 @@ static void mtip_hw_debugfs_exit(struct driver_data *dd)
debugfs_remove_recursive(dd->dfs_node);
}
-static int mtip_free_orphan(struct driver_data *dd)
-{
- struct kobject *kobj;
-
- if (dd->bdev) {
- if (dd->bdev->bd_holders >= 1)
- return -2;
-
- bdput(dd->bdev);
- dd->bdev = NULL;
- }
-
- mtip_hw_debugfs_exit(dd);
-
- spin_lock(&rssd_index_lock);
- ida_remove(&rssd_index_ida, dd->index);
- spin_unlock(&rssd_index_lock);
-
- if (!test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag) &&
- test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag)) {
- put_disk(dd->disk);
- } else {
- if (dd->disk) {
- kobj = kobject_get(&disk_to_dev(dd->disk)->kobj);
- if (kobj) {
- mtip_hw_sysfs_exit(dd, kobj);
- kobject_put(kobj);
- }
- del_gendisk(dd->disk);
- dd->disk = NULL;
- }
- if (dd->queue) {
- dd->queue->queuedata = NULL;
- blk_cleanup_queue(dd->queue);
- blk_mq_free_tag_set(&dd->tags);
- dd->queue = NULL;
- }
- }
- kfree(dd);
- return 0;
-}
-
/*
* Perform any init/resume time hardware setup
*
@@ -2944,7 +2888,6 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd)
mtip_block_initialize(dd);
return 0;
}
- ssleep(10);
} while (time_before(jiffies, timeout));
/* Check for timeout */
@@ -2969,7 +2912,6 @@ static int mtip_service_thread(void *data)
unsigned long slot, slot_start, slot_wrap;
unsigned int num_cmd_slots = dd->slot_groups * 32;
struct mtip_port *port = dd->port;
- int ret;
while (1) {
if (kthread_should_stop() ||
@@ -2990,10 +2932,6 @@ static int mtip_service_thread(void *data)
test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags))
goto st_out;
- /* If I am an orphan, start self cleanup */
- if (test_bit(MTIP_PF_SR_CLEANUP_BIT, &port->flags))
- break;
-
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
&dd->dd_flag)))
goto st_out;
@@ -3047,26 +2985,6 @@ restart_eh:
}
}
- /* wait for pci remove to exit */
- while (1) {
- if (test_bit(MTIP_DDF_REMOVE_DONE_BIT, &dd->dd_flag))
- break;
- msleep_interruptible(1000);
- if (kthread_should_stop())
- goto st_out;
- }
-
- while (1) {
- ret = mtip_free_orphan(dd);
- if (!ret) {
- /* NOTE: All data structures are invalid, do not
- * access any here */
- return 0;
- }
- msleep_interruptible(1000);
- if (kthread_should_stop())
- goto st_out;
- }
st_out:
return 0;
}
@@ -3394,6 +3312,7 @@ static int mtip_hw_exit(struct driver_data *dd)
/* Release the IRQ. */
irq_set_affinity_hint(dd->pdev->irq, NULL);
devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd);
+ msleep(1000);
/* Free dma regions */
mtip_dma_free(dd);
@@ -3699,6 +3618,26 @@ static const struct block_device_operations mtip_block_ops = {
.owner = THIS_MODULE
};
+static inline bool is_se_active(struct driver_data *dd)
+{
+ if (unlikely(test_bit(MTIP_PF_SE_ACTIVE_BIT, &dd->port->flags))) {
+ if (dd->port->ic_pause_timer) {
+ unsigned long to = dd->port->ic_pause_timer +
+ msecs_to_jiffies(1000);
+ if (time_after(jiffies, to)) {
+ clear_bit(MTIP_PF_SE_ACTIVE_BIT,
+ &dd->port->flags);
+ clear_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag);
+ dd->port->ic_pause_timer = 0;
+ wake_up_interruptible(&dd->port->svc_wait);
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
/*
* Block layer make request function.
*
@@ -3716,6 +3655,9 @@ static int mtip_submit_request(struct blk_mq_hw_ctx *hctx, struct request *rq)
struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq);
unsigned int nents;
+ if (is_se_active(dd))
+ return -ENODATA;
+
if (unlikely(dd->dd_flag & MTIP_DDF_STOP_IO)) {
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
&dd->dd_flag))) {
@@ -3900,7 +3842,8 @@ static int mtip_block_initialize(struct driver_data *dd)
dd->disk->driverfs_dev = &dd->pdev->dev;
dd->disk->major = dd->major;
- dd->disk->first_minor = dd->instance * MTIP_MAX_MINORS;
+ dd->disk->first_minor = index * MTIP_MAX_MINORS;
+ dd->disk->minors = MTIP_MAX_MINORS;
dd->disk->fops = &mtip_block_ops;
dd->disk->private_data = dd;
dd->index = index;
@@ -4066,52 +4009,51 @@ static int mtip_block_remove(struct driver_data *dd)
{
struct kobject *kobj;
- if (!dd->sr) {
- mtip_hw_debugfs_exit(dd);
+ mtip_hw_debugfs_exit(dd);
- if (dd->mtip_svc_handler) {
- set_bit(MTIP_PF_SVC_THD_STOP_BIT, &dd->port->flags);
- wake_up_interruptible(&dd->port->svc_wait);
- kthread_stop(dd->mtip_svc_handler);
- }
+ if (dd->mtip_svc_handler) {
+ set_bit(MTIP_PF_SVC_THD_STOP_BIT, &dd->port->flags);
+ wake_up_interruptible(&dd->port->svc_wait);
+ kthread_stop(dd->mtip_svc_handler);
+ }
- /* Clean up the sysfs attributes, if created */
- if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) {
- kobj = kobject_get(&disk_to_dev(dd->disk)->kobj);
- if (kobj) {
- mtip_hw_sysfs_exit(dd, kobj);
- kobject_put(kobj);
- }
+ /* Clean up the sysfs attributes, if created */
+ if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) {
+ kobj = kobject_get(&disk_to_dev(dd->disk)->kobj);
+ if (kobj) {
+ mtip_hw_sysfs_exit(dd, kobj);
+ kobject_put(kobj);
}
+ }
+ if (!dd->sr)
mtip_standby_drive(dd);
-
- /*
- * Delete our gendisk structure. This also removes the device
- * from /dev
- */
- if (dd->bdev) {
- bdput(dd->bdev);
- dd->bdev = NULL;
- }
- if (dd->disk) {
- if (dd->disk->queue) {
- del_gendisk(dd->disk);
- blk_cleanup_queue(dd->queue);
- blk_mq_free_tag_set(&dd->tags);
- dd->queue = NULL;
- } else
- put_disk(dd->disk);
- }
- dd->disk = NULL;
-
- spin_lock(&rssd_index_lock);
- ida_remove(&rssd_index_ida, dd->index);
- spin_unlock(&rssd_index_lock);
- } else {
+ else
dev_info(&dd->pdev->dev, "device %s surprise removal\n",
dd->disk->disk_name);
+
+ /*
+ * Delete our gendisk structure. This also removes the device
+ * from /dev
+ */
+ if (dd->bdev) {
+ bdput(dd->bdev);
+ dd->bdev = NULL;
}
+ if (dd->disk) {
+ del_gendisk(dd->disk);
+ if (dd->disk->queue) {
+ blk_cleanup_queue(dd->queue);
+ blk_mq_free_tag_set(&dd->tags);
+ dd->queue = NULL;
+ }
+ put_disk(dd->disk);
+ }
+ dd->disk = NULL;
+
+ spin_lock(&rssd_index_lock);
+ ida_remove(&rssd_index_ida, dd->index);
+ spin_unlock(&rssd_index_lock);
/* De-initialize the protocol layer. */
mtip_hw_exit(dd);
@@ -4140,12 +4082,12 @@ static int mtip_block_shutdown(struct driver_data *dd)
dev_info(&dd->pdev->dev,
"Shutting down %s ...\n", dd->disk->disk_name);
+ del_gendisk(dd->disk);
if (dd->disk->queue) {
- del_gendisk(dd->disk);
blk_cleanup_queue(dd->queue);
blk_mq_free_tag_set(&dd->tags);
- } else
- put_disk(dd->disk);
+ }
+ put_disk(dd->disk);
dd->disk = NULL;
dd->queue = NULL;
}
@@ -4507,6 +4449,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
"Completion workers still active!\n");
}
+ blk_mq_stop_hw_queues(dd->queue);
/* Clean up the block layer. */
mtip_block_remove(dd);
@@ -4524,10 +4467,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
list_del_init(&dd->remove_list);
spin_unlock_irqrestore(&dev_lock, flags);
- if (!dd->sr)
- kfree(dd);
- else
- set_bit(MTIP_DDF_REMOVE_DONE_BIT, &dd->dd_flag);
+ kfree(dd);
pcim_iounmap_regions(pdev, 1 << MTIP_ABAR);
pci_set_drvdata(pdev, NULL);
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index ba1b31e..3274784 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -142,7 +142,6 @@ enum {
MTIP_PF_SVC_THD_ACTIVE_BIT = 4,
MTIP_PF_ISSUE_CMDS_BIT = 5,
MTIP_PF_REBUILD_BIT = 6,
- MTIP_PF_SR_CLEANUP_BIT = 7,
MTIP_PF_SVC_THD_STOP_BIT = 8,
/* below are bit numbers in 'dd_flag' defined in driver_data */
@@ -150,7 +149,6 @@ enum {
MTIP_DDF_REMOVE_PENDING_BIT = 1,
MTIP_DDF_OVER_TEMP_BIT = 2,
MTIP_DDF_WRITE_PROTECT_BIT = 3,
- MTIP_DDF_REMOVE_DONE_BIT = 4,
MTIP_DDF_CLEANUP_BIT = 5,
MTIP_DDF_RESUME_BIT = 6,
MTIP_DDF_INIT_DONE_BIT = 7,
@@ -412,19 +410,13 @@ struct mtip_port {
* by the DMA when the driver issues internal commands.
*/
dma_addr_t sector_buffer_dma;
- /*
- * Bit significant, used to determine if a command slot has
- * been allocated. i.e. the slot is in use. Bits are cleared
- * when the command slot and all associated data structures
- * are no longer needed.
- */
+
u16 *log_buf;
dma_addr_t log_buf_dma;
u8 *smart_buf;
dma_addr_t smart_buf_dma;
- unsigned long allocated[SLOTBITS_IN_LONGS];
/*
* used to queue commands when an internal command is in progress
* or error handling is active
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 39e5f7f..0e385d8 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -230,29 +230,40 @@ static int nbd_send_req(struct nbd_device *nbd, struct request *req)
int result, flags;
struct nbd_request request;
unsigned long size = blk_rq_bytes(req);
+ u32 type;
+
+ if (req->cmd_type == REQ_TYPE_DRV_PRIV)
+ type = NBD_CMD_DISC;
+ else if (req->cmd_flags & REQ_DISCARD)
+ type = NBD_CMD_TRIM;
+ else if (req->cmd_flags & REQ_FLUSH)
+ type = NBD_CMD_FLUSH;
+ else if (rq_data_dir(req) == WRITE)
+ type = NBD_CMD_WRITE;
+ else
+ type = NBD_CMD_READ;
memset(&request, 0, sizeof(request));
request.magic = htonl(NBD_REQUEST_MAGIC);
- request.type = htonl(nbd_cmd(req));
-
- if (nbd_cmd(req) != NBD_CMD_FLUSH && nbd_cmd(req) != NBD_CMD_DISC) {
+ request.type = htonl(type);
+ if (type != NBD_CMD_FLUSH && type != NBD_CMD_DISC) {
request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9);
request.len = htonl(size);
}
memcpy(request.handle, &req, sizeof(req));
dev_dbg(nbd_to_dev(nbd), "request %p: sending control (%s@%llu,%uB)\n",
- req, nbdcmd_to_ascii(nbd_cmd(req)),
+ req, nbdcmd_to_ascii(type),
(unsigned long long)blk_rq_pos(req) << 9, blk_rq_bytes(req));
result = sock_xmit(nbd, 1, &request, sizeof(request),
- (nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0);
+ (type == NBD_CMD_WRITE) ? MSG_MORE : 0);
if (result <= 0) {
dev_err(disk_to_dev(nbd->disk),
"Send control failed (result %d)\n", result);
return -EIO;
}
- if (nbd_cmd(req) == NBD_CMD_WRITE) {
+ if (type == NBD_CMD_WRITE) {
struct req_iterator iter;
struct bio_vec bvec;
/*
@@ -352,7 +363,7 @@ static struct request *nbd_read_stat(struct nbd_device *nbd)
}
dev_dbg(nbd_to_dev(nbd), "request %p: got reply\n", req);
- if (nbd_cmd(req) == NBD_CMD_READ) {
+ if (rq_data_dir(req) != WRITE) {
struct req_iterator iter;
struct bio_vec bvec;
@@ -452,23 +463,11 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req)
if (req->cmd_type != REQ_TYPE_FS)
goto error_out;
- nbd_cmd(req) = NBD_CMD_READ;
- if (rq_data_dir(req) == WRITE) {
- if ((req->cmd_flags & REQ_DISCARD)) {
- WARN_ON(!(nbd->flags & NBD_FLAG_SEND_TRIM));
- nbd_cmd(req) = NBD_CMD_TRIM;
- } else
- nbd_cmd(req) = NBD_CMD_WRITE;
- if (nbd->flags & NBD_FLAG_READ_ONLY) {
- dev_err(disk_to_dev(nbd->disk),
- "Write on read-only\n");
- goto error_out;
- }
- }
-
- if (req->cmd_flags & REQ_FLUSH) {
- BUG_ON(unlikely(blk_rq_sectors(req)));
- nbd_cmd(req) = NBD_CMD_FLUSH;
+ if (rq_data_dir(req) == WRITE &&
+ (nbd->flags & NBD_FLAG_READ_ONLY)) {
+ dev_err(disk_to_dev(nbd->disk),
+ "Write on read-only\n");
+ goto error_out;
}
req->errors = 0;
@@ -592,8 +591,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
fsync_bdev(bdev);
mutex_lock(&nbd->tx_lock);
blk_rq_init(NULL, &sreq);
- sreq.cmd_type = REQ_TYPE_SPECIAL;
- nbd_cmd(&sreq) = NBD_CMD_DISC;
+ sreq.cmd_type = REQ_TYPE_DRV_PRIV;
/* Check again after getting mutex back. */
if (!nbd->sock)
@@ -713,7 +711,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
bdev->bd_inode->i_size = 0;
set_capacity(nbd->disk, 0);
if (max_part > 0)
- ioctl_by_bdev(bdev, BLKRRPART, 0);
+ blkdev_reread_part(bdev);
if (nbd->disconnect) /* user requested, ignore socket errors */
return 0;
return nbd->harderror;
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 65cd61a..6f9b753 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -243,6 +243,17 @@ static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer)
cmd = container_of(entry, struct nullb_cmd, ll_list);
entry = entry->next;
end_cmd(cmd);
+
+ if (cmd->rq) {
+ struct request_queue *q = cmd->rq->q;
+
+ if (!q->mq_ops && blk_queue_stopped(q)) {
+ spin_lock(q->queue_lock);
+ if (blk_queue_stopped(q))
+ blk_start_queue(q);
+ spin_unlock(q->queue_lock);
+ }
+ }
} while (entry);
}
@@ -257,7 +268,7 @@ static void null_cmd_end_timer(struct nullb_cmd *cmd)
if (llist_add(&cmd->ll_list, &cq->list)) {
ktime_t kt = ktime_set(0, completion_nsec);
- hrtimer_start(&cq->timer, kt, HRTIMER_MODE_REL);
+ hrtimer_start(&cq->timer, kt, HRTIMER_MODE_REL_PINNED);
}
put_cpu();
@@ -334,6 +345,7 @@ static int null_rq_prep_fn(struct request_queue *q, struct request *req)
req->special = cmd;
return BLKPREP_OK;
}
+ blk_stop_queue(q);
return BLKPREP_DEFER;
}
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c
index 683dff2..e511271 100644
--- a/drivers/block/nvme-core.c
+++ b/drivers/block/nvme-core.c
@@ -29,6 +29,7 @@
#include <linux/kdev_t.h>
#include <linux/kthread.h>
#include <linux/kernel.h>
+#include <linux/list_sort.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -80,6 +81,7 @@ static wait_queue_head_t nvme_kthread_wait;
static struct class *nvme_class;
static void nvme_reset_failed_dev(struct work_struct *ws);
+static int nvme_reset(struct nvme_dev *dev);
static int nvme_process_cq(struct nvme_queue *nvmeq);
struct async_cmd_info {
@@ -102,6 +104,7 @@ struct nvme_queue {
spinlock_t q_lock;
struct nvme_command *sq_cmds;
volatile struct nvme_completion *cqes;
+ struct blk_mq_tags **tags;
dma_addr_t sq_dma_addr;
dma_addr_t cq_dma_addr;
u32 __iomem *q_db;
@@ -114,7 +117,6 @@ struct nvme_queue {
u8 cq_phase;
u8 cqe_seen;
struct async_cmd_info cmdinfo;
- struct blk_mq_hw_ctx *hctx;
};
/*
@@ -182,9 +184,12 @@ static int nvme_admin_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
struct nvme_dev *dev = data;
struct nvme_queue *nvmeq = dev->queues[0];
- WARN_ON(nvmeq->hctx);
- nvmeq->hctx = hctx;
+ WARN_ON(hctx_idx != 0);
+ WARN_ON(dev->admin_tagset.tags[0] != hctx->tags);
+ WARN_ON(nvmeq->tags);
+
hctx->driver_data = nvmeq;
+ nvmeq->tags = &dev->admin_tagset.tags[0];
return 0;
}
@@ -201,27 +206,16 @@ static int nvme_admin_init_request(void *data, struct request *req,
return 0;
}
-static void nvme_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx)
-{
- struct nvme_queue *nvmeq = hctx->driver_data;
-
- nvmeq->hctx = NULL;
-}
-
static int nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
unsigned int hctx_idx)
{
struct nvme_dev *dev = data;
- struct nvme_queue *nvmeq = dev->queues[
- (hctx_idx % dev->queue_count) + 1];
-
- if (!nvmeq->hctx)
- nvmeq->hctx = hctx;
+ struct nvme_queue *nvmeq = dev->queues[hctx_idx + 1];
- /* nvmeq queues are shared between namespaces. We assume here that
- * blk-mq map the tags so they match up with the nvme queue tags. */
- WARN_ON(nvmeq->hctx->tags != hctx->tags);
+ if (!nvmeq->tags)
+ nvmeq->tags = &dev->tagset.tags[hctx_idx];
+ WARN_ON(dev->tagset.tags[hctx_idx] != hctx->tags);
hctx->driver_data = nvmeq;
return 0;
}
@@ -307,9 +301,16 @@ static void async_req_completion(struct nvme_queue *nvmeq, void *ctx,
if (status == NVME_SC_SUCCESS || status == NVME_SC_ABORT_REQ)
++nvmeq->dev->event_limit;
- if (status == NVME_SC_SUCCESS)
- dev_warn(nvmeq->q_dmadev,
- "async event result %08x\n", result);
+ if (status != NVME_SC_SUCCESS)
+ return;
+
+ switch (result & 0xff07) {
+ case NVME_AER_NOTICE_NS_CHANGED:
+ dev_info(nvmeq->q_dmadev, "rescanning\n");
+ schedule_work(&nvmeq->dev->scan_work);
+ default:
+ dev_warn(nvmeq->q_dmadev, "async event result %08x\n", result);
+ }
}
static void abort_completion(struct nvme_queue *nvmeq, void *ctx,
@@ -320,7 +321,7 @@ static void abort_completion(struct nvme_queue *nvmeq, void *ctx,
u16 status = le16_to_cpup(&cqe->status) >> 1;
u32 result = le32_to_cpup(&cqe->result);
- blk_mq_free_hctx_request(nvmeq->hctx, req);
+ blk_mq_free_request(req);
dev_warn(nvmeq->q_dmadev, "Abort status:%x result:%x", status, result);
++nvmeq->dev->abort_limit;
@@ -333,14 +334,13 @@ static void async_completion(struct nvme_queue *nvmeq, void *ctx,
cmdinfo->result = le32_to_cpup(&cqe->result);
cmdinfo->status = le16_to_cpup(&cqe->status) >> 1;
queue_kthread_work(cmdinfo->worker, &cmdinfo->work);
- blk_mq_free_hctx_request(nvmeq->hctx, cmdinfo->req);
+ blk_mq_free_request(cmdinfo->req);
}
static inline struct nvme_cmd_info *get_cmd_from_tag(struct nvme_queue *nvmeq,
unsigned int tag)
{
- struct blk_mq_hw_ctx *hctx = nvmeq->hctx;
- struct request *req = blk_mq_tag_to_rq(hctx->tags, tag);
+ struct request *req = blk_mq_tag_to_rq(*nvmeq->tags, tag);
return blk_mq_rq_to_pdu(req);
}
@@ -445,7 +445,7 @@ static struct nvme_iod *nvme_alloc_iod(struct request *rq, struct nvme_dev *dev,
(unsigned long) rq, gfp);
}
-void nvme_free_iod(struct nvme_dev *dev, struct nvme_iod *iod)
+static void nvme_free_iod(struct nvme_dev *dev, struct nvme_iod *iod)
{
const int last_prp = dev->page_size / 8 - 1;
int i;
@@ -605,22 +605,30 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
spin_unlock_irqrestore(req->q->queue_lock, flags);
return;
}
- req->errors = nvme_error_status(status);
+ if (req->cmd_type == REQ_TYPE_DRV_PRIV) {
+ req->errors = status;
+ } else {
+ req->errors = nvme_error_status(status);
+ }
} else
req->errors = 0;
+ if (req->cmd_type == REQ_TYPE_DRV_PRIV) {
+ u32 result = le32_to_cpup(&cqe->result);
+ req->special = (void *)(uintptr_t)result;
+ }
if (cmd_rq->aborted)
- dev_warn(&nvmeq->dev->pci_dev->dev,
+ dev_warn(nvmeq->dev->dev,
"completing aborted command with status:%04x\n",
status);
if (iod->nents) {
- dma_unmap_sg(&nvmeq->dev->pci_dev->dev, iod->sg, iod->nents,
+ dma_unmap_sg(nvmeq->dev->dev, iod->sg, iod->nents,
rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (blk_integrity_rq(req)) {
if (!rq_data_dir(req))
nvme_dif_remap(req, nvme_dif_complete);
- dma_unmap_sg(&nvmeq->dev->pci_dev->dev, iod->meta_sg, 1,
+ dma_unmap_sg(nvmeq->dev->dev, iod->meta_sg, 1,
rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
}
@@ -630,8 +638,8 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
}
/* length is in bytes. gfp flags indicates whether we may sleep. */
-int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, int total_len,
- gfp_t gfp)
+static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,
+ int total_len, gfp_t gfp)
{
struct dma_pool *pool;
int length = total_len;
@@ -709,6 +717,23 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, int total_len,
return total_len;
}
+static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
+ struct nvme_iod *iod)
+{
+ struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
+
+ memcpy(cmnd, req->cmd, sizeof(struct nvme_command));
+ cmnd->rw.command_id = req->tag;
+ if (req->nr_phys_segments) {
+ cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ cmnd->rw.prp2 = cpu_to_le64(iod->first_dma);
+ }
+
+ if (++nvmeq->sq_tail == nvmeq->q_depth)
+ nvmeq->sq_tail = 0;
+ writel(nvmeq->sq_tail, nvmeq->q_db);
+}
+
/*
* We reuse the small pool to allocate the 16-byte range here as it is not
* worth having a special pool for these or additional cases to handle freeing
@@ -807,11 +832,15 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
return 0;
}
+/*
+ * NOTE: ns is NULL when called on the admin queue.
+ */
static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct nvme_ns *ns = hctx->queue->queuedata;
struct nvme_queue *nvmeq = hctx->driver_data;
+ struct nvme_dev *dev = nvmeq->dev;
struct request *req = bd->rq;
struct nvme_cmd_info *cmd = blk_mq_rq_to_pdu(req);
struct nvme_iod *iod;
@@ -822,15 +851,16 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
* unless this namespace is formated such that the metadata can be
* stripped/generated by the controller with PRACT=1.
*/
- if (ns->ms && !blk_integrity_rq(req)) {
- if (!(ns->pi_type && ns->ms == 8)) {
+ if (ns && ns->ms && !blk_integrity_rq(req)) {
+ if (!(ns->pi_type && ns->ms == 8) &&
+ req->cmd_type != REQ_TYPE_DRV_PRIV) {
req->errors = -EFAULT;
blk_mq_complete_request(req);
return BLK_MQ_RQ_QUEUE_OK;
}
}
- iod = nvme_alloc_iod(req, ns->dev, GFP_ATOMIC);
+ iod = nvme_alloc_iod(req, dev, GFP_ATOMIC);
if (!iod)
return BLK_MQ_RQ_QUEUE_BUSY;
@@ -841,8 +871,7 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
* as it is not worth having a special pool for these or
* additional cases to handle freeing the iod.
*/
- range = dma_pool_alloc(nvmeq->dev->prp_small_pool,
- GFP_ATOMIC,
+ range = dma_pool_alloc(dev->prp_small_pool, GFP_ATOMIC,
&iod->first_dma);
if (!range)
goto retry_cmd;
@@ -860,9 +889,8 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
goto retry_cmd;
if (blk_rq_bytes(req) !=
- nvme_setup_prps(nvmeq->dev, iod, blk_rq_bytes(req), GFP_ATOMIC)) {
- dma_unmap_sg(&nvmeq->dev->pci_dev->dev, iod->sg,
- iod->nents, dma_dir);
+ nvme_setup_prps(dev, iod, blk_rq_bytes(req), GFP_ATOMIC)) {
+ dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
goto retry_cmd;
}
if (blk_integrity_rq(req)) {
@@ -884,7 +912,9 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
nvme_set_info(cmd, iod, req_completion);
spin_lock_irq(&nvmeq->q_lock);
- if (req->cmd_flags & REQ_DISCARD)
+ if (req->cmd_type == REQ_TYPE_DRV_PRIV)
+ nvme_submit_priv(nvmeq, req, iod);
+ else if (req->cmd_flags & REQ_DISCARD)
nvme_submit_discard(nvmeq, ns, req, iod);
else if (req->cmd_flags & REQ_FLUSH)
nvme_submit_flush(nvmeq, ns, req->tag);
@@ -896,10 +926,10 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
return BLK_MQ_RQ_QUEUE_OK;
error_cmd:
- nvme_free_iod(nvmeq->dev, iod);
+ nvme_free_iod(dev, iod);
return BLK_MQ_RQ_QUEUE_ERROR;
retry_cmd:
- nvme_free_iod(nvmeq->dev, iod);
+ nvme_free_iod(dev, iod);
return BLK_MQ_RQ_QUEUE_BUSY;
}
@@ -942,15 +972,6 @@ static int nvme_process_cq(struct nvme_queue *nvmeq)
return 1;
}
-/* Admin queue isn't initialized as a request queue. If at some point this
- * happens anyway, make sure to notify the user */
-static int nvme_admin_queue_rq(struct blk_mq_hw_ctx *hctx,
- const struct blk_mq_queue_data *bd)
-{
- WARN_ON_ONCE(1);
- return BLK_MQ_RQ_QUEUE_ERROR;
-}
-
static irqreturn_t nvme_irq(int irq, void *data)
{
irqreturn_t result;
@@ -972,46 +993,61 @@ static irqreturn_t nvme_irq_check(int irq, void *data)
return IRQ_WAKE_THREAD;
}
-struct sync_cmd_info {
- struct task_struct *task;
- u32 result;
- int status;
-};
-
-static void sync_completion(struct nvme_queue *nvmeq, void *ctx,
- struct nvme_completion *cqe)
-{
- struct sync_cmd_info *cmdinfo = ctx;
- cmdinfo->result = le32_to_cpup(&cqe->result);
- cmdinfo->status = le16_to_cpup(&cqe->status) >> 1;
- wake_up_process(cmdinfo->task);
-}
-
/*
* Returns 0 on success. If the result is negative, it's a Linux error code;
* if the result is positive, it's an NVM Express status code
*/
-static int nvme_submit_sync_cmd(struct request *req, struct nvme_command *cmd,
- u32 *result, unsigned timeout)
+int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd,
+ void *buffer, void __user *ubuffer, unsigned bufflen,
+ u32 *result, unsigned timeout)
{
- struct sync_cmd_info cmdinfo;
- struct nvme_cmd_info *cmd_rq = blk_mq_rq_to_pdu(req);
- struct nvme_queue *nvmeq = cmd_rq->nvmeq;
+ bool write = cmd->common.opcode & 1;
+ struct bio *bio = NULL;
+ struct request *req;
+ int ret;
- cmdinfo.task = current;
- cmdinfo.status = -EINTR;
+ req = blk_mq_alloc_request(q, write, GFP_KERNEL, false);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
- cmd->common.command_id = req->tag;
+ req->cmd_type = REQ_TYPE_DRV_PRIV;
+ req->cmd_flags |= REQ_FAILFAST_DRIVER;
+ req->__data_len = 0;
+ req->__sector = (sector_t) -1;
+ req->bio = req->biotail = NULL;
+
+ req->timeout = timeout ? timeout : ADMIN_TIMEOUT;
- nvme_set_info(cmd_rq, &cmdinfo, sync_completion);
+ req->cmd = (unsigned char *)cmd;
+ req->cmd_len = sizeof(struct nvme_command);
+ req->special = (void *)0;
- set_current_state(TASK_UNINTERRUPTIBLE);
- nvme_submit_cmd(nvmeq, cmd);
- schedule();
+ if (buffer && bufflen) {
+ ret = blk_rq_map_kern(q, req, buffer, bufflen, __GFP_WAIT);
+ if (ret)
+ goto out;
+ } else if (ubuffer && bufflen) {
+ ret = blk_rq_map_user(q, req, NULL, ubuffer, bufflen, __GFP_WAIT);
+ if (ret)
+ goto out;
+ bio = req->bio;
+ }
+ blk_execute_rq(req->q, NULL, req, 0);
+ if (bio)
+ blk_rq_unmap_user(bio);
if (result)
- *result = cmdinfo.result;
- return cmdinfo.status;
+ *result = (u32)(uintptr_t)req->special;
+ ret = req->errors;
+ out:
+ blk_mq_free_request(req);
+ return ret;
+}
+
+int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd,
+ void *buffer, unsigned bufflen)
+{
+ return __nvme_submit_sync_cmd(q, cmd, buffer, NULL, bufflen, NULL, 0);
}
static int nvme_submit_async_admin_req(struct nvme_dev *dev)
@@ -1033,7 +1069,7 @@ static int nvme_submit_async_admin_req(struct nvme_dev *dev)
c.common.opcode = nvme_admin_async_event;
c.common.command_id = req->tag;
- blk_mq_free_hctx_request(nvmeq->hctx, req);
+ blk_mq_free_request(req);
return __nvme_submit_cmd(nvmeq, &c);
}
@@ -1060,41 +1096,6 @@ static int nvme_submit_admin_async_cmd(struct nvme_dev *dev,
return nvme_submit_cmd(nvmeq, cmd);
}
-static int __nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
- u32 *result, unsigned timeout)
-{
- int res;
- struct request *req;
-
- req = blk_mq_alloc_request(dev->admin_q, WRITE, GFP_KERNEL, false);
- if (IS_ERR(req))
- return PTR_ERR(req);
- res = nvme_submit_sync_cmd(req, cmd, result, timeout);
- blk_mq_free_request(req);
- return res;
-}
-
-int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
- u32 *result)
-{
- return __nvme_submit_admin_cmd(dev, cmd, result, ADMIN_TIMEOUT);
-}
-
-int nvme_submit_io_cmd(struct nvme_dev *dev, struct nvme_ns *ns,
- struct nvme_command *cmd, u32 *result)
-{
- int res;
- struct request *req;
-
- req = blk_mq_alloc_request(ns->queue, WRITE, (GFP_KERNEL|__GFP_WAIT),
- false);
- if (IS_ERR(req))
- return PTR_ERR(req);
- res = nvme_submit_sync_cmd(req, cmd, result, NVME_IO_TIMEOUT);
- blk_mq_free_request(req);
- return res;
-}
-
static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
{
struct nvme_command c;
@@ -1103,7 +1104,7 @@ static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
c.delete_queue.opcode = opcode;
c.delete_queue.qid = cpu_to_le16(id);
- return nvme_submit_admin_cmd(dev, &c, NULL);
+ return nvme_submit_sync_cmd(dev->admin_q, &c, NULL, 0);
}
static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid,
@@ -1112,6 +1113,10 @@ static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid,
struct nvme_command c;
int flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED;
+ /*
+ * Note: we (ab)use the fact the the prp fields survive if no data
+ * is attached to the request.
+ */
memset(&c, 0, sizeof(c));
c.create_cq.opcode = nvme_admin_create_cq;
c.create_cq.prp1 = cpu_to_le64(nvmeq->cq_dma_addr);
@@ -1120,7 +1125,7 @@ static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid,
c.create_cq.cq_flags = cpu_to_le16(flags);
c.create_cq.irq_vector = cpu_to_le16(nvmeq->cq_vector);
- return nvme_submit_admin_cmd(dev, &c, NULL);
+ return nvme_submit_sync_cmd(dev->admin_q, &c, NULL, 0);
}
static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid,
@@ -1129,6 +1134,10 @@ static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid,
struct nvme_command c;
int flags = NVME_QUEUE_PHYS_CONTIG | NVME_SQ_PRIO_MEDIUM;
+ /*
+ * Note: we (ab)use the fact the the prp fields survive if no data
+ * is attached to the request.
+ */
memset(&c, 0, sizeof(c));
c.create_sq.opcode = nvme_admin_create_sq;
c.create_sq.prp1 = cpu_to_le64(nvmeq->sq_dma_addr);
@@ -1137,7 +1146,7 @@ static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid,
c.create_sq.sq_flags = cpu_to_le16(flags);
c.create_sq.cqid = cpu_to_le16(qid);
- return nvme_submit_admin_cmd(dev, &c, NULL);
+ return nvme_submit_sync_cmd(dev->admin_q, &c, NULL, 0);
}
static int adapter_delete_cq(struct nvme_dev *dev, u16 cqid)
@@ -1150,18 +1159,43 @@ static int adapter_delete_sq(struct nvme_dev *dev, u16 sqid)
return adapter_delete_queue(dev, nvme_admin_delete_sq, sqid);
}
-int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,
- dma_addr_t dma_addr)
+int nvme_identify_ctrl(struct nvme_dev *dev, struct nvme_id_ctrl **id)
{
- struct nvme_command c;
+ struct nvme_command c = {
+ .identify.opcode = nvme_admin_identify,
+ .identify.cns = cpu_to_le32(1),
+ };
+ int error;
- memset(&c, 0, sizeof(c));
- c.identify.opcode = nvme_admin_identify;
- c.identify.nsid = cpu_to_le32(nsid);
- c.identify.prp1 = cpu_to_le64(dma_addr);
- c.identify.cns = cpu_to_le32(cns);
+ *id = kmalloc(sizeof(struct nvme_id_ctrl), GFP_KERNEL);
+ if (!*id)
+ return -ENOMEM;
- return nvme_submit_admin_cmd(dev, &c, NULL);
+ error = nvme_submit_sync_cmd(dev->admin_q, &c, *id,
+ sizeof(struct nvme_id_ctrl));
+ if (error)
+ kfree(*id);
+ return error;
+}
+
+int nvme_identify_ns(struct nvme_dev *dev, unsigned nsid,
+ struct nvme_id_ns **id)
+{
+ struct nvme_command c = {
+ .identify.opcode = nvme_admin_identify,
+ .identify.nsid = cpu_to_le32(nsid),
+ };
+ int error;
+
+ *id = kmalloc(sizeof(struct nvme_id_ns), GFP_KERNEL);
+ if (!*id)
+ return -ENOMEM;
+
+ error = nvme_submit_sync_cmd(dev->admin_q, &c, *id,
+ sizeof(struct nvme_id_ns));
+ if (error)
+ kfree(*id);
+ return error;
}
int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid,
@@ -1175,7 +1209,8 @@ int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid,
c.features.prp1 = cpu_to_le64(dma_addr);
c.features.fid = cpu_to_le32(fid);
- return nvme_submit_admin_cmd(dev, &c, result);
+ return __nvme_submit_sync_cmd(dev->admin_q, &c, NULL, NULL, 0,
+ result, 0);
}
int nvme_set_features(struct nvme_dev *dev, unsigned fid, unsigned dword11,
@@ -1189,7 +1224,30 @@ int nvme_set_features(struct nvme_dev *dev, unsigned fid, unsigned dword11,
c.features.fid = cpu_to_le32(fid);
c.features.dword11 = cpu_to_le32(dword11);
- return nvme_submit_admin_cmd(dev, &c, result);
+ return __nvme_submit_sync_cmd(dev->admin_q, &c, NULL, NULL, 0,
+ result, 0);
+}
+
+int nvme_get_log_page(struct nvme_dev *dev, struct nvme_smart_log **log)
+{
+ struct nvme_command c = {
+ .common.opcode = nvme_admin_get_log_page,
+ .common.nsid = cpu_to_le32(0xFFFFFFFF),
+ .common.cdw10[0] = cpu_to_le32(
+ (((sizeof(struct nvme_smart_log) / 4) - 1) << 16) |
+ NVME_LOG_SMART),
+ };
+ int error;
+
+ *log = kmalloc(sizeof(struct nvme_smart_log), GFP_KERNEL);
+ if (!*log)
+ return -ENOMEM;
+
+ error = nvme_submit_sync_cmd(dev->admin_q, &c, *log,
+ sizeof(struct nvme_smart_log));
+ if (error)
+ kfree(*log);
+ return error;
}
/**
@@ -1214,8 +1272,7 @@ static void nvme_abort_req(struct request *req)
if (work_busy(&dev->reset_work))
goto out;
list_del_init(&dev->node);
- dev_warn(&dev->pci_dev->dev,
- "I/O %d QID %d timeout, reset controller\n",
+ dev_warn(dev->dev, "I/O %d QID %d timeout, reset controller\n",
req->tag, nvmeq->qid);
dev->reset_workfn = nvme_reset_failed_dev;
queue_work(nvme_workq, &dev->reset_work);
@@ -1254,8 +1311,7 @@ static void nvme_abort_req(struct request *req)
}
}
-static void nvme_cancel_queue_ios(struct blk_mq_hw_ctx *hctx,
- struct request *req, void *data, bool reserved)
+static void nvme_cancel_queue_ios(struct request *req, void *data, bool reserved)
{
struct nvme_queue *nvmeq = data;
void *ctx;
@@ -1352,11 +1408,9 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq)
static void nvme_clear_queue(struct nvme_queue *nvmeq)
{
- struct blk_mq_hw_ctx *hctx = nvmeq->hctx;
-
spin_lock_irq(&nvmeq->q_lock);
- if (hctx && hctx->tags)
- blk_mq_tag_busy_iter(hctx, nvme_cancel_queue_ios, nvmeq);
+ if (nvmeq->tags && *nvmeq->tags)
+ blk_mq_all_tag_busy_iter(*nvmeq->tags, nvme_cancel_queue_ios, nvmeq);
spin_unlock_irq(&nvmeq->q_lock);
}
@@ -1384,22 +1438,21 @@ static void nvme_disable_queue(struct nvme_dev *dev, int qid)
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
int depth)
{
- struct device *dmadev = &dev->pci_dev->dev;
struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq), GFP_KERNEL);
if (!nvmeq)
return NULL;
- nvmeq->cqes = dma_zalloc_coherent(dmadev, CQ_SIZE(depth),
+ nvmeq->cqes = dma_zalloc_coherent(dev->dev, CQ_SIZE(depth),
&nvmeq->cq_dma_addr, GFP_KERNEL);
if (!nvmeq->cqes)
goto free_nvmeq;
- nvmeq->sq_cmds = dma_alloc_coherent(dmadev, SQ_SIZE(depth),
+ nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
&nvmeq->sq_dma_addr, GFP_KERNEL);
if (!nvmeq->sq_cmds)
goto free_cqdma;
- nvmeq->q_dmadev = dmadev;
+ nvmeq->q_dmadev = dev->dev;
nvmeq->dev = dev;
snprintf(nvmeq->irqname, sizeof(nvmeq->irqname), "nvme%dq%d",
dev->instance, qid);
@@ -1409,13 +1462,16 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
nvmeq->q_depth = depth;
nvmeq->qid = qid;
- dev->queue_count++;
dev->queues[qid] = nvmeq;
+ /* make sure queue descriptor is set before queue count, for kthread */
+ mb();
+ dev->queue_count++;
+
return nvmeq;
free_cqdma:
- dma_free_coherent(dmadev, CQ_SIZE(depth), (void *)nvmeq->cqes,
+ dma_free_coherent(dev->dev, CQ_SIZE(depth), (void *)nvmeq->cqes,
nvmeq->cq_dma_addr);
free_nvmeq:
kfree(nvmeq);
@@ -1487,7 +1543,7 @@ static int nvme_wait_ready(struct nvme_dev *dev, u64 cap, bool enabled)
if (fatal_signal_pending(current))
return -EINTR;
if (time_after(jiffies, timeout)) {
- dev_err(&dev->pci_dev->dev,
+ dev_err(dev->dev,
"Device not ready; aborting %s\n", enabled ?
"initialisation" : "reset");
return -ENODEV;
@@ -1537,7 +1593,7 @@ static int nvme_shutdown_ctrl(struct nvme_dev *dev)
if (fatal_signal_pending(current))
return -EINTR;
if (time_after(jiffies, timeout)) {
- dev_err(&dev->pci_dev->dev,
+ dev_err(dev->dev,
"Device shutdown incomplete; abort shutdown\n");
return -ENODEV;
}
@@ -1547,10 +1603,9 @@ static int nvme_shutdown_ctrl(struct nvme_dev *dev)
}
static struct blk_mq_ops nvme_mq_admin_ops = {
- .queue_rq = nvme_admin_queue_rq,
+ .queue_rq = nvme_queue_rq,
.map_queue = blk_mq_map_queue,
.init_hctx = nvme_admin_init_hctx,
- .exit_hctx = nvme_exit_hctx,
.init_request = nvme_admin_init_request,
.timeout = nvme_timeout,
};
@@ -1559,7 +1614,6 @@ static struct blk_mq_ops nvme_mq_ops = {
.queue_rq = nvme_queue_rq,
.map_queue = blk_mq_map_queue,
.init_hctx = nvme_init_hctx,
- .exit_hctx = nvme_exit_hctx,
.init_request = nvme_init_request,
.timeout = nvme_timeout,
};
@@ -1580,7 +1634,7 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev)
dev->admin_tagset.queue_depth = NVME_AQ_DEPTH - 1;
dev->admin_tagset.reserved_tags = 1;
dev->admin_tagset.timeout = ADMIN_TIMEOUT;
- dev->admin_tagset.numa_node = dev_to_node(&dev->pci_dev->dev);
+ dev->admin_tagset.numa_node = dev_to_node(dev->dev);
dev->admin_tagset.cmd_size = nvme_cmd_size(dev);
dev->admin_tagset.driver_data = dev;
@@ -1613,14 +1667,14 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
unsigned dev_page_max = NVME_CAP_MPSMAX(cap) + 12;
if (page_shift < dev_page_min) {
- dev_err(&dev->pci_dev->dev,
+ dev_err(dev->dev,
"Minimum device page size (%u) too large for "
"host (%u)\n", 1 << dev_page_min,
1 << page_shift);
return -ENODEV;
}
if (page_shift > dev_page_max) {
- dev_info(&dev->pci_dev->dev,
+ dev_info(dev->dev,
"Device maximum page size (%u) smaller than "
"host (%u); enabling work-around\n",
1 << dev_page_max, 1 << page_shift);
@@ -1668,126 +1722,43 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
return result;
}
-struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
- unsigned long addr, unsigned length)
-{
- int i, err, count, nents, offset;
- struct scatterlist *sg;
- struct page **pages;
- struct nvme_iod *iod;
-
- if (addr & 3)
- return ERR_PTR(-EINVAL);
- if (!length || length > INT_MAX - PAGE_SIZE)
- return ERR_PTR(-EINVAL);
-
- offset = offset_in_page(addr);
- count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
- pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
- if (!pages)
- return ERR_PTR(-ENOMEM);
-
- err = get_user_pages_fast(addr, count, 1, pages);
- if (err < count) {
- count = err;
- err = -EFAULT;
- goto put_pages;
- }
-
- err = -ENOMEM;
- iod = __nvme_alloc_iod(count, length, dev, 0, GFP_KERNEL);
- if (!iod)
- goto put_pages;
-
- sg = iod->sg;
- sg_init_table(sg, count);
- for (i = 0; i < count; i++) {
- sg_set_page(&sg[i], pages[i],
- min_t(unsigned, length, PAGE_SIZE - offset),
- offset);
- length -= (PAGE_SIZE - offset);
- offset = 0;
- }
- sg_mark_end(&sg[i - 1]);
- iod->nents = count;
-
- nents = dma_map_sg(&dev->pci_dev->dev, sg, count,
- write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (!nents)
- goto free_iod;
-
- kfree(pages);
- return iod;
-
- free_iod:
- kfree(iod);
- put_pages:
- for (i = 0; i < count; i++)
- put_page(pages[i]);
- kfree(pages);
- return ERR_PTR(err);
-}
-
-void nvme_unmap_user_pages(struct nvme_dev *dev, int write,
- struct nvme_iod *iod)
-{
- int i;
-
- dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents,
- write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
-
- for (i = 0; i < iod->nents; i++)
- put_page(sg_page(&iod->sg[i]));
-}
-
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
{
struct nvme_dev *dev = ns->dev;
struct nvme_user_io io;
struct nvme_command c;
- unsigned length, meta_len, prp_len;
+ unsigned length, meta_len;
int status, write;
- struct nvme_iod *iod;
dma_addr_t meta_dma = 0;
void *meta = NULL;
void __user *metadata;
if (copy_from_user(&io, uio, sizeof(io)))
return -EFAULT;
- length = (io.nblocks + 1) << ns->lba_shift;
- meta_len = (io.nblocks + 1) * ns->ms;
-
- if (meta_len && ((io.metadata & 3) || !io.metadata) && !ns->ext)
- return -EINVAL;
- else if (meta_len && ns->ext) {
- length += meta_len;
- meta_len = 0;
- }
-
- metadata = (void __user *)(unsigned long)io.metadata;
-
- write = io.opcode & 1;
switch (io.opcode) {
case nvme_cmd_write:
case nvme_cmd_read:
case nvme_cmd_compare:
- iod = nvme_map_user_pages(dev, write, io.addr, length);
break;
default:
return -EINVAL;
}
- if (IS_ERR(iod))
- return PTR_ERR(iod);
+ length = (io.nblocks + 1) << ns->lba_shift;
+ meta_len = (io.nblocks + 1) * ns->ms;
+ metadata = (void __user *)(unsigned long)io.metadata;
+ write = io.opcode & 1;
- prp_len = nvme_setup_prps(dev, iod, length, GFP_KERNEL);
- if (length != prp_len) {
- status = -ENOMEM;
- goto unmap;
+ if (ns->ext) {
+ length += meta_len;
+ meta_len = 0;
}
if (meta_len) {
- meta = dma_alloc_coherent(&dev->pci_dev->dev, meta_len,
+ if (((io.metadata & 3) || !io.metadata) && !ns->ext)
+ return -EINVAL;
+
+ meta = dma_alloc_coherent(dev->dev, meta_len,
&meta_dma, GFP_KERNEL);
if (!meta) {
@@ -1813,19 +1784,17 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
c.rw.reftag = cpu_to_le32(io.reftag);
c.rw.apptag = cpu_to_le16(io.apptag);
c.rw.appmask = cpu_to_le16(io.appmask);
- c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
- c.rw.prp2 = cpu_to_le64(iod->first_dma);
c.rw.metadata = cpu_to_le64(meta_dma);
- status = nvme_submit_io_cmd(dev, ns, &c, NULL);
+
+ status = __nvme_submit_sync_cmd(ns->queue, &c, NULL,
+ (void __user *)io.addr, length, NULL, 0);
unmap:
- nvme_unmap_user_pages(dev, write, iod);
- nvme_free_iod(dev, iod);
if (meta) {
if (status == NVME_SC_SUCCESS && !write) {
if (copy_to_user(metadata, meta, meta_len))
status = -EFAULT;
}
- dma_free_coherent(&dev->pci_dev->dev, meta_len, meta, meta_dma);
+ dma_free_coherent(dev->dev, meta_len, meta, meta_dma);
}
return status;
}
@@ -1835,9 +1804,8 @@ static int nvme_user_cmd(struct nvme_dev *dev, struct nvme_ns *ns,
{
struct nvme_passthru_cmd cmd;
struct nvme_command c;
- int status, length;
- struct nvme_iod *uninitialized_var(iod);
- unsigned timeout;
+ unsigned timeout = 0;
+ int status;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -1857,46 +1825,17 @@ static int nvme_user_cmd(struct nvme_dev *dev, struct nvme_ns *ns,
c.common.cdw10[4] = cpu_to_le32(cmd.cdw14);
c.common.cdw10[5] = cpu_to_le32(cmd.cdw15);
- length = cmd.data_len;
- if (cmd.data_len) {
- iod = nvme_map_user_pages(dev, cmd.opcode & 1, cmd.addr,
- length);
- if (IS_ERR(iod))
- return PTR_ERR(iod);
- length = nvme_setup_prps(dev, iod, length, GFP_KERNEL);
- c.common.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
- c.common.prp2 = cpu_to_le64(iod->first_dma);
- }
-
- timeout = cmd.timeout_ms ? msecs_to_jiffies(cmd.timeout_ms) :
- ADMIN_TIMEOUT;
-
- if (length != cmd.data_len)
- status = -ENOMEM;
- else if (ns) {
- struct request *req;
-
- req = blk_mq_alloc_request(ns->queue, WRITE,
- (GFP_KERNEL|__GFP_WAIT), false);
- if (IS_ERR(req))
- status = PTR_ERR(req);
- else {
- status = nvme_submit_sync_cmd(req, &c, &cmd.result,
- timeout);
- blk_mq_free_request(req);
- }
- } else
- status = __nvme_submit_admin_cmd(dev, &c, &cmd.result, timeout);
+ if (cmd.timeout_ms)
+ timeout = msecs_to_jiffies(cmd.timeout_ms);
- if (cmd.data_len) {
- nvme_unmap_user_pages(dev, cmd.opcode & 1, iod);
- nvme_free_iod(dev, iod);
+ status = __nvme_submit_sync_cmd(ns ? ns->queue : dev->admin_q, &c,
+ NULL, (void __user *)cmd.addr, cmd.data_len,
+ &cmd.result, timeout);
+ if (status >= 0) {
+ if (put_user(cmd.result, &ucmd->result))
+ return -EFAULT;
}
- if ((status >= 0) && copy_to_user(&ucmd->result, &cmd.result,
- sizeof(cmd.result)))
- status = -EFAULT;
-
return status;
}
@@ -1988,23 +1927,18 @@ static int nvme_revalidate_disk(struct gendisk *disk)
struct nvme_ns *ns = disk->private_data;
struct nvme_dev *dev = ns->dev;
struct nvme_id_ns *id;
- dma_addr_t dma_addr;
u8 lbaf, pi_type;
u16 old_ms;
unsigned short bs;
- id = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr,
- GFP_KERNEL);
- if (!id) {
- dev_warn(&dev->pci_dev->dev, "%s: Memory alocation failure\n",
- __func__);
- return 0;
+ if (nvme_identify_ns(dev, ns->ns_id, &id)) {
+ dev_warn(dev->dev, "%s: Identify failure nvme%dn%d\n", __func__,
+ dev->instance, ns->ns_id);
+ return -ENODEV;
}
- if (nvme_identify(dev, ns->ns_id, 0, dma_addr)) {
- dev_warn(&dev->pci_dev->dev,
- "identify failed ns:%d, setting capacity to 0\n",
- ns->ns_id);
- memset(id, 0, sizeof(*id));
+ if (id->ncap == 0) {
+ kfree(id);
+ return -ENODEV;
}
old_ms = ns->ms;
@@ -2038,7 +1972,7 @@ static int nvme_revalidate_disk(struct gendisk *disk)
!ns->ext)
nvme_init_integrity(ns);
- if (id->ncap == 0 || (ns->ms && !blk_get_integrity(disk)))
+ if (ns->ms && !blk_get_integrity(disk))
set_capacity(disk, 0);
else
set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
@@ -2046,7 +1980,7 @@ static int nvme_revalidate_disk(struct gendisk *disk)
if (dev->oncs & NVME_CTRL_ONCS_DSM)
nvme_config_discard(ns);
- dma_free_coherent(&dev->pci_dev->dev, 4096, id, dma_addr);
+ kfree(id);
return 0;
}
@@ -2073,7 +2007,7 @@ static int nvme_kthread(void *data)
if (work_busy(&dev->reset_work))
continue;
list_del_init(&dev->node);
- dev_warn(&dev->pci_dev->dev,
+ dev_warn(dev->dev,
"Failed status: %x, reset controller\n",
readl(&dev->bar->csts));
dev->reset_workfn = nvme_reset_failed_dev;
@@ -2105,7 +2039,7 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
{
struct nvme_ns *ns;
struct gendisk *disk;
- int node = dev_to_node(&dev->pci_dev->dev);
+ int node = dev_to_node(dev->dev);
ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
if (!ns)
@@ -2153,11 +2087,16 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
* requires it.
*/
set_capacity(disk, 0);
- nvme_revalidate_disk(ns->disk);
+ if (nvme_revalidate_disk(ns->disk))
+ goto out_free_disk;
+
add_disk(ns->disk);
if (ns->ms)
revalidate_disk(ns->disk);
return;
+ out_free_disk:
+ kfree(disk);
+ list_del(&ns->list);
out_free_queue:
blk_cleanup_queue(ns->queue);
out_free_ns:
@@ -2188,8 +2127,7 @@ static int set_queue_count(struct nvme_dev *dev, int count)
if (status < 0)
return status;
if (status > 0) {
- dev_err(&dev->pci_dev->dev, "Could not set queue count (%d)\n",
- status);
+ dev_err(dev->dev, "Could not set queue count (%d)\n", status);
return 0;
}
return min(result & 0xffff, result >> 16) + 1;
@@ -2203,7 +2141,7 @@ static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
static int nvme_setup_io_queues(struct nvme_dev *dev)
{
struct nvme_queue *adminq = dev->queues[0];
- struct pci_dev *pdev = dev->pci_dev;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
int result, i, vecs, nr_io_queues, size;
nr_io_queues = num_possible_cpus();
@@ -2275,6 +2213,99 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
return result;
}
+static void nvme_free_namespace(struct nvme_ns *ns)
+{
+ list_del(&ns->list);
+
+ spin_lock(&dev_list_lock);
+ ns->disk->private_data = NULL;
+ spin_unlock(&dev_list_lock);
+
+ put_disk(ns->disk);
+ kfree(ns);
+}
+
+static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct nvme_ns *nsa = container_of(a, struct nvme_ns, list);
+ struct nvme_ns *nsb = container_of(b, struct nvme_ns, list);
+
+ return nsa->ns_id - nsb->ns_id;
+}
+
+static struct nvme_ns *nvme_find_ns(struct nvme_dev *dev, unsigned nsid)
+{
+ struct nvme_ns *ns;
+
+ list_for_each_entry(ns, &dev->namespaces, list) {
+ if (ns->ns_id == nsid)
+ return ns;
+ if (ns->ns_id > nsid)
+ break;
+ }
+ return NULL;
+}
+
+static inline bool nvme_io_incapable(struct nvme_dev *dev)
+{
+ return (!dev->bar || readl(&dev->bar->csts) & NVME_CSTS_CFS ||
+ dev->online_queues < 2);
+}
+
+static void nvme_ns_remove(struct nvme_ns *ns)
+{
+ bool kill = nvme_io_incapable(ns->dev) && !blk_queue_dying(ns->queue);
+
+ if (kill)
+ blk_set_queue_dying(ns->queue);
+ if (ns->disk->flags & GENHD_FL_UP) {
+ if (blk_get_integrity(ns->disk))
+ blk_integrity_unregister(ns->disk);
+ del_gendisk(ns->disk);
+ }
+ if (kill || !blk_queue_dying(ns->queue)) {
+ blk_mq_abort_requeue_list(ns->queue);
+ blk_cleanup_queue(ns->queue);
+ }
+}
+
+static void nvme_scan_namespaces(struct nvme_dev *dev, unsigned nn)
+{
+ struct nvme_ns *ns, *next;
+ unsigned i;
+
+ for (i = 1; i <= nn; i++) {
+ ns = nvme_find_ns(dev, i);
+ if (ns) {
+ if (revalidate_disk(ns->disk)) {
+ nvme_ns_remove(ns);
+ nvme_free_namespace(ns);
+ }
+ } else
+ nvme_alloc_ns(dev, i);
+ }
+ list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
+ if (ns->ns_id > nn) {
+ nvme_ns_remove(ns);
+ nvme_free_namespace(ns);
+ }
+ }
+ list_sort(NULL, &dev->namespaces, ns_cmp);
+}
+
+static void nvme_dev_scan(struct work_struct *work)
+{
+ struct nvme_dev *dev = container_of(work, struct nvme_dev, scan_work);
+ struct nvme_id_ctrl *ctrl;
+
+ if (!dev->tagset.tags)
+ return;
+ if (nvme_identify_ctrl(dev, &ctrl))
+ return;
+ nvme_scan_namespaces(dev, le32_to_cpup(&ctrl->nn));
+ kfree(ctrl);
+}
+
/*
* Return: error value if an error occurred setting up the queues or calling
* Identify Device. 0 if these succeeded, even if adding some of the
@@ -2283,26 +2314,18 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
*/
static int nvme_dev_add(struct nvme_dev *dev)
{
- struct pci_dev *pdev = dev->pci_dev;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
int res;
- unsigned nn, i;
+ unsigned nn;
struct nvme_id_ctrl *ctrl;
- void *mem;
- dma_addr_t dma_addr;
int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
- mem = dma_alloc_coherent(&pdev->dev, 4096, &dma_addr, GFP_KERNEL);
- if (!mem)
- return -ENOMEM;
-
- res = nvme_identify(dev, 0, 1, dma_addr);
+ res = nvme_identify_ctrl(dev, &ctrl);
if (res) {
- dev_err(&pdev->dev, "Identify Controller failed (%d)\n", res);
- dma_free_coherent(&dev->pci_dev->dev, 4096, mem, dma_addr);
+ dev_err(dev->dev, "Identify Controller failed (%d)\n", res);
return -EIO;
}
- ctrl = mem;
nn = le32_to_cpup(&ctrl->nn);
dev->oncs = le16_to_cpup(&ctrl->oncs);
dev->abort_limit = ctrl->acl + 1;
@@ -2324,12 +2347,12 @@ static int nvme_dev_add(struct nvme_dev *dev)
} else
dev->max_hw_sectors = max_hw_sectors;
}
- dma_free_coherent(&dev->pci_dev->dev, 4096, mem, dma_addr);
+ kfree(ctrl);
dev->tagset.ops = &nvme_mq_ops;
dev->tagset.nr_hw_queues = dev->online_queues - 1;
dev->tagset.timeout = NVME_IO_TIMEOUT;
- dev->tagset.numa_node = dev_to_node(&dev->pci_dev->dev);
+ dev->tagset.numa_node = dev_to_node(dev->dev);
dev->tagset.queue_depth =
min_t(int, dev->q_depth, BLK_MQ_MAX_DEPTH) - 1;
dev->tagset.cmd_size = nvme_cmd_size(dev);
@@ -2339,9 +2362,7 @@ static int nvme_dev_add(struct nvme_dev *dev)
if (blk_mq_alloc_tag_set(&dev->tagset))
return 0;
- for (i = 1; i <= nn; i++)
- nvme_alloc_ns(dev, i);
-
+ schedule_work(&dev->scan_work);
return 0;
}
@@ -2349,7 +2370,7 @@ static int nvme_dev_map(struct nvme_dev *dev)
{
u64 cap;
int bars, result = -ENOMEM;
- struct pci_dev *pdev = dev->pci_dev;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
if (pci_enable_device_mem(pdev))
return result;
@@ -2363,8 +2384,8 @@ static int nvme_dev_map(struct nvme_dev *dev)
if (pci_request_selected_regions(pdev, bars, "nvme"))
goto disable_pci;
- if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)) &&
- dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+ if (dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(64)) &&
+ dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32)))
goto disable;
dev->bar = ioremap(pci_resource_start(pdev, 0), 8192);
@@ -2405,19 +2426,21 @@ static int nvme_dev_map(struct nvme_dev *dev)
static void nvme_dev_unmap(struct nvme_dev *dev)
{
- if (dev->pci_dev->msi_enabled)
- pci_disable_msi(dev->pci_dev);
- else if (dev->pci_dev->msix_enabled)
- pci_disable_msix(dev->pci_dev);
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+
+ if (pdev->msi_enabled)
+ pci_disable_msi(pdev);
+ else if (pdev->msix_enabled)
+ pci_disable_msix(pdev);
if (dev->bar) {
iounmap(dev->bar);
dev->bar = NULL;
- pci_release_regions(dev->pci_dev);
+ pci_release_regions(pdev);
}
- if (pci_is_enabled(dev->pci_dev))
- pci_disable_device(dev->pci_dev);
+ if (pci_is_enabled(pdev))
+ pci_disable_device(pdev);
}
struct nvme_delq_ctx {
@@ -2536,7 +2559,7 @@ static void nvme_disable_io_queues(struct nvme_dev *dev)
&worker, "nvme%d", dev->instance);
if (IS_ERR(kworker_task)) {
- dev_err(&dev->pci_dev->dev,
+ dev_err(dev->dev,
"Failed to create queue del task\n");
for (i = dev->queue_count - 1; i > 0; i--)
nvme_disable_queue(dev, i);
@@ -2587,9 +2610,9 @@ static void nvme_freeze_queues(struct nvme_dev *dev)
list_for_each_entry(ns, &dev->namespaces, list) {
blk_mq_freeze_queue_start(ns->queue);
- spin_lock(ns->queue->queue_lock);
+ spin_lock_irq(ns->queue->queue_lock);
queue_flag_set(QUEUE_FLAG_STOPPED, ns->queue);
- spin_unlock(ns->queue->queue_lock);
+ spin_unlock_irq(ns->queue->queue_lock);
blk_mq_cancel_requeue_work(ns->queue);
blk_mq_stop_hw_queues(ns->queue);
@@ -2639,29 +2662,19 @@ static void nvme_dev_remove(struct nvme_dev *dev)
{
struct nvme_ns *ns;
- list_for_each_entry(ns, &dev->namespaces, list) {
- if (ns->disk->flags & GENHD_FL_UP) {
- if (blk_get_integrity(ns->disk))
- blk_integrity_unregister(ns->disk);
- del_gendisk(ns->disk);
- }
- if (!blk_queue_dying(ns->queue)) {
- blk_mq_abort_requeue_list(ns->queue);
- blk_cleanup_queue(ns->queue);
- }
- }
+ list_for_each_entry(ns, &dev->namespaces, list)
+ nvme_ns_remove(ns);
}
static int nvme_setup_prp_pools(struct nvme_dev *dev)
{
- struct device *dmadev = &dev->pci_dev->dev;
- dev->prp_page_pool = dma_pool_create("prp list page", dmadev,
+ dev->prp_page_pool = dma_pool_create("prp list page", dev->dev,
PAGE_SIZE, PAGE_SIZE, 0);
if (!dev->prp_page_pool)
return -ENOMEM;
/* Optimisation for I/Os between 4k and 128k */
- dev->prp_small_pool = dma_pool_create("prp list 256", dmadev,
+ dev->prp_small_pool = dma_pool_create("prp list 256", dev->dev,
256, 256, 0);
if (!dev->prp_small_pool) {
dma_pool_destroy(dev->prp_page_pool);
@@ -2709,23 +2722,15 @@ static void nvme_free_namespaces(struct nvme_dev *dev)
{
struct nvme_ns *ns, *next;
- list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
- list_del(&ns->list);
-
- spin_lock(&dev_list_lock);
- ns->disk->private_data = NULL;
- spin_unlock(&dev_list_lock);
-
- put_disk(ns->disk);
- kfree(ns);
- }
+ list_for_each_entry_safe(ns, next, &dev->namespaces, list)
+ nvme_free_namespace(ns);
}
static void nvme_free_dev(struct kref *kref)
{
struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref);
- pci_dev_put(dev->pci_dev);
+ put_device(dev->dev);
put_device(dev->device);
nvme_free_namespaces(dev);
nvme_release_instance(dev);
@@ -2781,6 +2786,9 @@ static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -ENOTTY;
ns = list_first_entry(&dev->namespaces, struct nvme_ns, list);
return nvme_user_cmd(dev, ns, (void __user *)arg);
+ case NVME_IOCTL_RESET:
+ dev_warn(dev->dev, "resetting controller\n");
+ return nvme_reset(dev);
default:
return -ENOTTY;
}
@@ -2802,11 +2810,11 @@ static void nvme_set_irq_hints(struct nvme_dev *dev)
for (i = 0; i < dev->online_queues; i++) {
nvmeq = dev->queues[i];
- if (!nvmeq->hctx)
+ if (!nvmeq->tags || !(*nvmeq->tags))
continue;
irq_set_affinity_hint(dev->entry[nvmeq->cq_vector].vector,
- nvmeq->hctx->cpumask);
+ blk_mq_tags_cpumask(*nvmeq->tags));
}
}
@@ -2869,7 +2877,7 @@ static int nvme_dev_start(struct nvme_dev *dev)
static int nvme_remove_dead_ctrl(void *arg)
{
struct nvme_dev *dev = (struct nvme_dev *)arg;
- struct pci_dev *pdev = dev->pci_dev;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
if (pci_get_drvdata(pdev))
pci_stop_and_remove_bus_device_locked(pdev);
@@ -2899,6 +2907,7 @@ static int nvme_dev_resume(struct nvme_dev *dev)
spin_unlock(&dev_list_lock);
} else {
nvme_unfreeze_queues(dev);
+ schedule_work(&dev->scan_work);
nvme_set_irq_hints(dev);
}
return 0;
@@ -2908,11 +2917,11 @@ static void nvme_dev_reset(struct nvme_dev *dev)
{
nvme_dev_shutdown(dev);
if (nvme_dev_resume(dev)) {
- dev_warn(&dev->pci_dev->dev, "Device failed to resume\n");
+ dev_warn(dev->dev, "Device failed to resume\n");
kref_get(&dev->kref);
if (IS_ERR(kthread_run(nvme_remove_dead_ctrl, dev, "nvme%d",
dev->instance))) {
- dev_err(&dev->pci_dev->dev,
+ dev_err(dev->dev,
"Failed to start controller remove task\n");
kref_put(&dev->kref, nvme_free_dev);
}
@@ -2931,6 +2940,44 @@ static void nvme_reset_workfn(struct work_struct *work)
dev->reset_workfn(work);
}
+static int nvme_reset(struct nvme_dev *dev)
+{
+ int ret = -EBUSY;
+
+ if (!dev->admin_q || blk_queue_dying(dev->admin_q))
+ return -ENODEV;
+
+ spin_lock(&dev_list_lock);
+ if (!work_pending(&dev->reset_work)) {
+ dev->reset_workfn = nvme_reset_failed_dev;
+ queue_work(nvme_workq, &dev->reset_work);
+ ret = 0;
+ }
+ spin_unlock(&dev_list_lock);
+
+ if (!ret) {
+ flush_work(&dev->reset_work);
+ return 0;
+ }
+
+ return ret;
+}
+
+static ssize_t nvme_sysfs_reset(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nvme_dev *ndev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = nvme_reset(ndev);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);
+
static void nvme_async_probe(struct work_struct *work);
static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
@@ -2956,7 +3003,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_LIST_HEAD(&dev->namespaces);
dev->reset_workfn = nvme_reset_failed_dev;
INIT_WORK(&dev->reset_work, nvme_reset_workfn);
- dev->pci_dev = pci_dev_get(pdev);
+ dev->dev = get_device(&pdev->dev);
pci_set_drvdata(pdev, dev);
result = nvme_set_instance(dev);
if (result)
@@ -2975,18 +3022,27 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto release_pools;
}
get_device(dev->device);
+ dev_set_drvdata(dev->device, dev);
+
+ result = device_create_file(dev->device, &dev_attr_reset_controller);
+ if (result)
+ goto put_dev;
INIT_LIST_HEAD(&dev->node);
+ INIT_WORK(&dev->scan_work, nvme_dev_scan);
INIT_WORK(&dev->probe_work, nvme_async_probe);
schedule_work(&dev->probe_work);
return 0;
+ put_dev:
+ device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance));
+ put_device(dev->device);
release_pools:
nvme_release_prp_pools(dev);
release:
nvme_release_instance(dev);
put_pci:
- pci_dev_put(dev->pci_dev);
+ put_device(dev->dev);
free:
kfree(dev->queues);
kfree(dev->entry);
@@ -3011,10 +3067,12 @@ static void nvme_async_probe(struct work_struct *work)
nvme_set_irq_hints(dev);
return;
reset:
+ spin_lock(&dev_list_lock);
if (!work_busy(&dev->reset_work)) {
dev->reset_workfn = nvme_reset_failed_dev;
queue_work(nvme_workq, &dev->reset_work);
}
+ spin_unlock(&dev_list_lock);
}
static void nvme_reset_notify(struct pci_dev *pdev, bool prepare)
@@ -3044,6 +3102,8 @@ static void nvme_remove(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
flush_work(&dev->probe_work);
flush_work(&dev->reset_work);
+ flush_work(&dev->scan_work);
+ device_remove_file(dev->device, &dev_attr_reset_controller);
nvme_dev_shutdown(dev);
nvme_dev_remove(dev);
nvme_dev_remove_admin(dev);
diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c
index 44f2514..e5a63f0 100644
--- a/drivers/block/nvme-scsi.c
+++ b/drivers/block/nvme-scsi.c
@@ -41,15 +41,13 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <asm/unaligned.h>
#include <scsi/sg.h>
#include <scsi/scsi.h>
static int sg_version_num = 30534; /* 2 digits for each component */
-#define SNTI_TRANSLATION_SUCCESS 0
-#define SNTI_INTERNAL_ERROR 1
-
/* VPD Page Codes */
#define VPD_SUPPORTED_PAGES 0x00
#define VPD_SERIAL_NUMBER 0x80
@@ -58,49 +56,14 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#define VPD_BLOCK_LIMITS 0xB0
#define VPD_BLOCK_DEV_CHARACTERISTICS 0xB1
-/* CDB offsets */
-#define REPORT_LUNS_CDB_ALLOC_LENGTH_OFFSET 6
-#define REPORT_LUNS_SR_OFFSET 2
-#define READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET 10
-#define REQUEST_SENSE_CDB_ALLOC_LENGTH_OFFSET 4
-#define REQUEST_SENSE_DESC_OFFSET 1
-#define REQUEST_SENSE_DESC_MASK 0x01
-#define DESCRIPTOR_FORMAT_SENSE_DATA_TYPE 1
-#define INQUIRY_EVPD_BYTE_OFFSET 1
-#define INQUIRY_PAGE_CODE_BYTE_OFFSET 2
-#define INQUIRY_EVPD_BIT_MASK 1
-#define INQUIRY_CDB_ALLOCATION_LENGTH_OFFSET 3
-#define START_STOP_UNIT_CDB_IMMED_OFFSET 1
-#define START_STOP_UNIT_CDB_IMMED_MASK 0x1
-#define START_STOP_UNIT_CDB_POWER_COND_MOD_OFFSET 3
-#define START_STOP_UNIT_CDB_POWER_COND_MOD_MASK 0xF
-#define START_STOP_UNIT_CDB_POWER_COND_OFFSET 4
-#define START_STOP_UNIT_CDB_POWER_COND_MASK 0xF0
-#define START_STOP_UNIT_CDB_NO_FLUSH_OFFSET 4
-#define START_STOP_UNIT_CDB_NO_FLUSH_MASK 0x4
-#define START_STOP_UNIT_CDB_START_OFFSET 4
-#define START_STOP_UNIT_CDB_START_MASK 0x1
-#define WRITE_BUFFER_CDB_MODE_OFFSET 1
-#define WRITE_BUFFER_CDB_MODE_MASK 0x1F
-#define WRITE_BUFFER_CDB_BUFFER_ID_OFFSET 2
-#define WRITE_BUFFER_CDB_BUFFER_OFFSET_OFFSET 3
-#define WRITE_BUFFER_CDB_PARM_LIST_LENGTH_OFFSET 6
-#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_OFFSET 1
-#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_MASK 0xC0
-#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_SHIFT 6
-#define FORMAT_UNIT_CDB_LONG_LIST_OFFSET 1
-#define FORMAT_UNIT_CDB_LONG_LIST_MASK 0x20
-#define FORMAT_UNIT_CDB_FORMAT_DATA_OFFSET 1
-#define FORMAT_UNIT_CDB_FORMAT_DATA_MASK 0x10
+/* format unit paramter list offsets */
#define FORMAT_UNIT_SHORT_PARM_LIST_LEN 4
#define FORMAT_UNIT_LONG_PARM_LIST_LEN 8
#define FORMAT_UNIT_PROT_INT_OFFSET 3
#define FORMAT_UNIT_PROT_FIELD_USAGE_OFFSET 0
#define FORMAT_UNIT_PROT_FIELD_USAGE_MASK 0x07
-#define UNMAP_CDB_PARAM_LIST_LENGTH_OFFSET 7
/* Misc. defines */
-#define NIBBLE_SHIFT 4
#define FIXED_SENSE_DATA 0x70
#define DESC_FORMAT_SENSE_DATA 0x72
#define FIXED_SENSE_DATA_ADD_LENGTH 10
@@ -144,27 +107,6 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#define EXTENDED_INQUIRY_DATA_PAGE_LENGTH 0x3C
#define RESERVED_FIELD 0
-/* SCSI READ/WRITE Defines */
-#define IO_CDB_WP_MASK 0xE0
-#define IO_CDB_WP_SHIFT 5
-#define IO_CDB_FUA_MASK 0x8
-#define IO_6_CDB_LBA_OFFSET 0
-#define IO_6_CDB_LBA_MASK 0x001FFFFF
-#define IO_6_CDB_TX_LEN_OFFSET 4
-#define IO_6_DEFAULT_TX_LEN 256
-#define IO_10_CDB_LBA_OFFSET 2
-#define IO_10_CDB_TX_LEN_OFFSET 7
-#define IO_10_CDB_WP_OFFSET 1
-#define IO_10_CDB_FUA_OFFSET 1
-#define IO_12_CDB_LBA_OFFSET 2
-#define IO_12_CDB_TX_LEN_OFFSET 6
-#define IO_12_CDB_WP_OFFSET 1
-#define IO_12_CDB_FUA_OFFSET 1
-#define IO_16_CDB_FUA_OFFSET 1
-#define IO_16_CDB_WP_OFFSET 1
-#define IO_16_CDB_LBA_OFFSET 2
-#define IO_16_CDB_TX_LEN_OFFSET 10
-
/* Mode Sense/Select defines */
#define MODE_PAGE_INFO_EXCEP 0x1C
#define MODE_PAGE_CACHING 0x08
@@ -179,23 +121,14 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#define MODE_PAGE_INF_EXC_LEN 0x0C
#define MODE_PAGE_ALL_LEN 0x54
#define MODE_SENSE6_MPH_SIZE 4
-#define MODE_SENSE6_ALLOC_LEN_OFFSET 4
-#define MODE_SENSE_PAGE_CONTROL_OFFSET 2
#define MODE_SENSE_PAGE_CONTROL_MASK 0xC0
#define MODE_SENSE_PAGE_CODE_OFFSET 2
#define MODE_SENSE_PAGE_CODE_MASK 0x3F
-#define MODE_SENSE_LLBAA_OFFSET 1
#define MODE_SENSE_LLBAA_MASK 0x10
#define MODE_SENSE_LLBAA_SHIFT 4
-#define MODE_SENSE_DBD_OFFSET 1
#define MODE_SENSE_DBD_MASK 8
#define MODE_SENSE_DBD_SHIFT 3
#define MODE_SENSE10_MPH_SIZE 8
-#define MODE_SENSE10_ALLOC_LEN_OFFSET 7
-#define MODE_SELECT_CDB_PAGE_FORMAT_OFFSET 1
-#define MODE_SELECT_CDB_SAVE_PAGES_OFFSET 1
-#define MODE_SELECT_6_CDB_PARAM_LIST_LENGTH_OFFSET 4
-#define MODE_SELECT_10_CDB_PARAM_LIST_LENGTH_OFFSET 7
#define MODE_SELECT_CDB_PAGE_FORMAT_MASK 0x10
#define MODE_SELECT_CDB_SAVE_PAGES_MASK 0x1
#define MODE_SELECT_6_BD_OFFSET 3
@@ -221,14 +154,11 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#define LOG_PAGE_SUPPORTED_LOG_PAGES_LENGTH 0x07
#define LOG_PAGE_INFORMATIONAL_EXCEPTIONS_PAGE 0x2F
#define LOG_PAGE_TEMPERATURE_PAGE 0x0D
-#define LOG_SENSE_CDB_SP_OFFSET 1
#define LOG_SENSE_CDB_SP_NOT_ENABLED 0
-#define LOG_SENSE_CDB_PC_OFFSET 2
#define LOG_SENSE_CDB_PC_MASK 0xC0
#define LOG_SENSE_CDB_PC_SHIFT 6
#define LOG_SENSE_CDB_PC_CUMULATIVE_VALUES 1
#define LOG_SENSE_CDB_PAGE_CODE_MASK 0x3F
-#define LOG_SENSE_CDB_ALLOC_LENGTH_OFFSET 7
#define REMAINING_INFO_EXCP_PAGE_LENGTH 0x8
#define LOG_INFO_EXCP_PAGE_LENGTH 0xC
#define REMAINING_TEMP_PAGE_LENGTH 0xC
@@ -278,77 +208,11 @@ static int sg_version_num = 30534; /* 2 digits for each component */
#define SCSI_ASCQ_POWER_LOSS_EXPECTED 0x08
#define SCSI_ASCQ_INVALID_LUN_ID 0x09
-/**
- * DEVICE_SPECIFIC_PARAMETER in mode parameter header (see sbc2r16) to
- * enable DPOFUA support type 0x10 value.
- */
-#define DEVICE_SPECIFIC_PARAMETER 0
-#define VPD_ID_DESCRIPTOR_LENGTH sizeof(VPD_IDENTIFICATION_DESCRIPTOR)
-
-/* MACROs to extract information from CDBs */
-
-#define GET_OPCODE(cdb) cdb[0]
-
-#define GET_U8_FROM_CDB(cdb, index) (cdb[index] << 0)
-
-#define GET_U16_FROM_CDB(cdb, index) ((cdb[index] << 8) | (cdb[index + 1] << 0))
-
-#define GET_U24_FROM_CDB(cdb, index) ((cdb[index] << 16) | \
-(cdb[index + 1] << 8) | \
-(cdb[index + 2] << 0))
-
-#define GET_U32_FROM_CDB(cdb, index) ((cdb[index] << 24) | \
-(cdb[index + 1] << 16) | \
-(cdb[index + 2] << 8) | \
-(cdb[index + 3] << 0))
-
-#define GET_U64_FROM_CDB(cdb, index) ((((u64)cdb[index]) << 56) | \
-(((u64)cdb[index + 1]) << 48) | \
-(((u64)cdb[index + 2]) << 40) | \
-(((u64)cdb[index + 3]) << 32) | \
-(((u64)cdb[index + 4]) << 24) | \
-(((u64)cdb[index + 5]) << 16) | \
-(((u64)cdb[index + 6]) << 8) | \
-(((u64)cdb[index + 7]) << 0))
-
-/* Inquiry Helper Macros */
-#define GET_INQ_EVPD_BIT(cdb) \
-((GET_U8_FROM_CDB(cdb, INQUIRY_EVPD_BYTE_OFFSET) & \
-INQUIRY_EVPD_BIT_MASK) ? 1 : 0)
-
-#define GET_INQ_PAGE_CODE(cdb) \
-(GET_U8_FROM_CDB(cdb, INQUIRY_PAGE_CODE_BYTE_OFFSET))
-
-#define GET_INQ_ALLOC_LENGTH(cdb) \
-(GET_U16_FROM_CDB(cdb, INQUIRY_CDB_ALLOCATION_LENGTH_OFFSET))
-
-/* Report LUNs Helper Macros */
-#define GET_REPORT_LUNS_ALLOC_LENGTH(cdb) \
-(GET_U32_FROM_CDB(cdb, REPORT_LUNS_CDB_ALLOC_LENGTH_OFFSET))
-
-/* Read Capacity Helper Macros */
-#define GET_READ_CAP_16_ALLOC_LENGTH(cdb) \
-(GET_U32_FROM_CDB(cdb, READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET))
-
-#define IS_READ_CAP_16(cdb) \
-((cdb[0] == SERVICE_ACTION_IN_16 && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0)
-
-/* Request Sense Helper Macros */
-#define GET_REQUEST_SENSE_ALLOC_LENGTH(cdb) \
-(GET_U8_FROM_CDB(cdb, REQUEST_SENSE_CDB_ALLOC_LENGTH_OFFSET))
-
-/* Mode Sense Helper Macros */
-#define GET_MODE_SENSE_DBD(cdb) \
-((GET_U8_FROM_CDB(cdb, MODE_SENSE_DBD_OFFSET) & MODE_SENSE_DBD_MASK) >> \
-MODE_SENSE_DBD_SHIFT)
-
-#define GET_MODE_SENSE_LLBAA(cdb) \
-((GET_U8_FROM_CDB(cdb, MODE_SENSE_LLBAA_OFFSET) & \
-MODE_SENSE_LLBAA_MASK) >> MODE_SENSE_LLBAA_SHIFT)
-
-#define GET_MODE_SENSE_MPH_SIZE(cdb10) \
-(cdb10 ? MODE_SENSE10_MPH_SIZE : MODE_SENSE6_MPH_SIZE)
-
+/* copied from drivers/usb/gadget/function/storage_common.h */
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
/* Struct to gather data that needs to be extracted from a SCSI CDB.
Not conforming to any particular CDB variant, but compatible with all. */
@@ -369,8 +233,6 @@ struct nvme_trans_io_cdb {
static int nvme_trans_copy_to_user(struct sg_io_hdr *hdr, void *from,
unsigned long n)
{
- int res = SNTI_TRANSLATION_SUCCESS;
- unsigned long not_copied;
int i;
void *index = from;
size_t remaining = n;
@@ -380,29 +242,25 @@ static int nvme_trans_copy_to_user(struct sg_io_hdr *hdr, void *from,
struct sg_iovec sgl;
for (i = 0; i < hdr->iovec_count; i++) {
- not_copied = copy_from_user(&sgl, hdr->dxferp +
+ if (copy_from_user(&sgl, hdr->dxferp +
i * sizeof(struct sg_iovec),
- sizeof(struct sg_iovec));
- if (not_copied)
+ sizeof(struct sg_iovec)))
return -EFAULT;
xfer_len = min(remaining, sgl.iov_len);
- not_copied = copy_to_user(sgl.iov_base, index,
- xfer_len);
- if (not_copied) {
- res = -EFAULT;
- break;
- }
+ if (copy_to_user(sgl.iov_base, index, xfer_len))
+ return -EFAULT;
+
index += xfer_len;
remaining -= xfer_len;
if (remaining == 0)
break;
}
- return res;
+ return 0;
}
- not_copied = copy_to_user(hdr->dxferp, from, n);
- if (not_copied)
- res = -EFAULT;
- return res;
+
+ if (copy_to_user(hdr->dxferp, from, n))
+ return -EFAULT;
+ return 0;
}
/* Copy data from userspace memory */
@@ -410,8 +268,6 @@ static int nvme_trans_copy_to_user(struct sg_io_hdr *hdr, void *from,
static int nvme_trans_copy_from_user(struct sg_io_hdr *hdr, void *to,
unsigned long n)
{
- int res = SNTI_TRANSLATION_SUCCESS;
- unsigned long not_copied;
int i;
void *index = to;
size_t remaining = n;
@@ -421,30 +277,24 @@ static int nvme_trans_copy_from_user(struct sg_io_hdr *hdr, void *to,
struct sg_iovec sgl;
for (i = 0; i < hdr->iovec_count; i++) {
- not_copied = copy_from_user(&sgl, hdr->dxferp +
+ if (copy_from_user(&sgl, hdr->dxferp +
i * sizeof(struct sg_iovec),
- sizeof(struct sg_iovec));
- if (not_copied)
+ sizeof(struct sg_iovec)))
return -EFAULT;
xfer_len = min(remaining, sgl.iov_len);
- not_copied = copy_from_user(index, sgl.iov_base,
- xfer_len);
- if (not_copied) {
- res = -EFAULT;
- break;
- }
+ if (copy_from_user(index, sgl.iov_base, xfer_len))
+ return -EFAULT;
index += xfer_len;
remaining -= xfer_len;
if (remaining == 0)
break;
}
- return res;
+ return 0;
}
- not_copied = copy_from_user(to, hdr->dxferp, n);
- if (not_copied)
- res = -EFAULT;
- return res;
+ if (copy_from_user(to, hdr->dxferp, n))
+ return -EFAULT;
+ return 0;
}
/* Status/Sense Buffer Writeback */
@@ -452,7 +302,6 @@ static int nvme_trans_copy_from_user(struct sg_io_hdr *hdr, void *to,
static int nvme_trans_completion(struct sg_io_hdr *hdr, u8 status, u8 sense_key,
u8 asc, u8 ascq)
{
- int res = SNTI_TRANSLATION_SUCCESS;
u8 xfer_len;
u8 resp[DESC_FMT_SENSE_DATA_SIZE];
@@ -477,25 +326,29 @@ static int nvme_trans_completion(struct sg_io_hdr *hdr, u8 status, u8 sense_key,
xfer_len = min_t(u8, hdr->mx_sb_len, DESC_FMT_SENSE_DATA_SIZE);
hdr->sb_len_wr = xfer_len;
if (copy_to_user(hdr->sbp, resp, xfer_len) > 0)
- res = -EFAULT;
+ return -EFAULT;
}
- return res;
+ return 0;
}
+/*
+ * Take a status code from a lowlevel routine, and if it was a positive NVMe
+ * error code update the sense data based on it. In either case the passed
+ * in value is returned again, unless an -EFAULT from copy_to_user overrides
+ * it.
+ */
static int nvme_trans_status_code(struct sg_io_hdr *hdr, int nvme_sc)
{
u8 status, sense_key, asc, ascq;
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
/* For non-nvme (Linux) errors, simply return the error code */
if (nvme_sc < 0)
return nvme_sc;
/* Mask DNR, More, and reserved fields */
- nvme_sc &= 0x7FF;
-
- switch (nvme_sc) {
+ switch (nvme_sc & 0x7FF) {
/* Generic Command Status */
case NVME_SC_SUCCESS:
status = SAM_STAT_GOOD;
@@ -662,8 +515,7 @@ static int nvme_trans_status_code(struct sg_io_hdr *hdr, int nvme_sc)
}
res = nvme_trans_completion(hdr, status, sense_key, asc, ascq);
-
- return res;
+ return res ? res : nvme_sc;
}
/* INQUIRY Helper Functions */
@@ -673,10 +525,8 @@ static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
int alloc_len)
{
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ns *id_ns;
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
int xfer_len;
u8 resp_data_format = 0x02;
@@ -684,31 +534,17 @@ static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
u8 cmdque = 0x01 << 1;
u8 fw_offset = sizeof(dev->firmware_rev);
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out_dma;
- }
-
/* nvme ns identify - use DPS value for PROTECT field */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
- /*
- * If nvme_sc was -ve, res will be -ve here.
- * If nvme_sc was +ve, the status would bace been translated, and res
- * can only be 0 or -ve.
- * - If 0 && nvme_sc > 0, then go into next if where res gets nvme_sc
- * - If -ve, return because its a Linux error.
- */
if (res)
- goto out_free;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_free;
- }
- id_ns = mem;
- (id_ns->dps) ? (protect = 0x01) : (protect = 0);
+ return res;
+
+ if (id_ns->dps)
+ protect = 0x01;
+ else
+ protect = 0;
+ kfree(id_ns);
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
inq_response[2] = VERSION_SPC_4;
@@ -725,20 +561,13 @@ static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
strncpy(&inq_response[32], dev->firmware_rev + fw_offset, 4);
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
- res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
-
- out_free:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out_dma:
- return res;
+ return nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
}
static int nvme_trans_supported_vpd_pages(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *inq_response,
int alloc_len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
@@ -752,9 +581,7 @@ static int nvme_trans_supported_vpd_pages(struct nvme_ns *ns,
inq_response[9] = INQ_BDEV_LIMITS_PAGE;
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
- res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
-
- return res;
+ return nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
}
static int nvme_trans_unit_serial_page(struct nvme_ns *ns,
@@ -762,7 +589,6 @@ static int nvme_trans_unit_serial_page(struct nvme_ns *ns,
int alloc_len)
{
struct nvme_dev *dev = ns->dev;
- int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
@@ -771,53 +597,42 @@ static int nvme_trans_unit_serial_page(struct nvme_ns *ns,
strncpy(&inq_response[4], dev->serial, INQ_SERIAL_NUMBER_LENGTH);
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
- res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
-
- return res;
+ return nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
}
static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *inq_response, int alloc_len)
{
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
int xfer_len;
__be32 tmp_id = cpu_to_be32(ns->ns_id);
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out_dma;
- }
-
memset(inq_response, 0, alloc_len);
inq_response[1] = INQ_DEVICE_IDENTIFICATION_PAGE; /* Page Code */
if (readl(&dev->bar->vs) >= NVME_VS(1, 1)) {
- struct nvme_id_ns *id_ns = mem;
- void *eui = id_ns->eui64;
- int len = sizeof(id_ns->eui64);
+ struct nvme_id_ns *id_ns;
+ void *eui;
+ int len;
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_free;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_free;
- }
+ return res;
+ eui = id_ns->eui64;
+ len = sizeof(id_ns->eui64);
if (readl(&dev->bar->vs) >= NVME_VS(1, 2)) {
if (bitmap_empty(eui, len * 8)) {
eui = id_ns->nguid;
len = sizeof(id_ns->nguid);
}
}
- if (bitmap_empty(eui, len * 8))
+ if (bitmap_empty(eui, len * 8)) {
+ kfree(id_ns);
goto scsi_string;
+ }
inq_response[3] = 4 + len; /* Page Length */
/* Designation Descriptor start */
@@ -826,14 +641,14 @@ static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
inq_response[6] = 0x00; /* Rsvd */
inq_response[7] = len; /* Designator Length */
memcpy(&inq_response[8], eui, len);
+ kfree(id_ns);
} else {
scsi_string:
if (alloc_len < 72) {
- res = nvme_trans_completion(hdr,
+ return nvme_trans_completion(hdr,
SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- goto out_free;
}
inq_response[3] = 0x48; /* Page Length */
/* Designation Descriptor start */
@@ -842,30 +657,22 @@ static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
inq_response[6] = 0x00; /* Rsvd */
inq_response[7] = 0x44; /* Designator Length */
- sprintf(&inq_response[8], "%04x", dev->pci_dev->vendor);
+ sprintf(&inq_response[8], "%04x", to_pci_dev(dev->dev)->vendor);
memcpy(&inq_response[12], dev->model, sizeof(dev->model));
sprintf(&inq_response[52], "%04x", tmp_id);
memcpy(&inq_response[56], dev->serial, sizeof(dev->serial));
}
xfer_len = alloc_len;
- res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
-
- out_free:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out_dma:
- return res;
+ return nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
}
static int nvme_trans_ext_inq_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
u8 *inq_response;
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ctrl *id_ctrl;
struct nvme_id_ns *id_ns;
int xfer_len;
@@ -878,45 +685,32 @@ static int nvme_trans_ext_inq_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 luiclr = 0x01;
inq_response = kmalloc(EXTENDED_INQUIRY_DATA_PAGE_LENGTH, GFP_KERNEL);
- if (inq_response == NULL) {
- res = -ENOMEM;
- goto out_mem;
- }
-
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out_dma;
- }
+ if (inq_response == NULL)
+ return -ENOMEM;
- /* nvme ns identify */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_free;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_free;
- }
- id_ns = mem;
- spt = spt_lut[(id_ns->dpc) & 0x07] << 3;
- (id_ns->dps) ? (protect = 0x01) : (protect = 0);
+ goto out_free_inq;
+
+ spt = spt_lut[id_ns->dpc & 0x07] << 3;
+ if (id_ns->dps)
+ protect = 0x01;
+ else
+ protect = 0;
+ kfree(id_ns);
+
grd_chk = protect << 2;
app_chk = protect << 1;
ref_chk = protect;
- /* nvme controller identify */
- nvme_sc = nvme_identify(dev, 0, 1, dma_addr);
+ nvme_sc = nvme_identify_ctrl(dev, &id_ctrl);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_free;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_free;
- }
- id_ctrl = mem;
+ goto out_free_inq;
+
v_sup = id_ctrl->vwc;
+ kfree(id_ctrl);
memset(inq_response, 0, EXTENDED_INQUIRY_DATA_PAGE_LENGTH);
inq_response[1] = INQ_EXTENDED_INQUIRY_DATA_PAGE; /* Page Code */
@@ -932,12 +726,8 @@ static int nvme_trans_ext_inq_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
xfer_len = min(alloc_len, EXTENDED_INQUIRY_DATA_PAGE_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
- out_free:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out_dma:
+ out_free_inq:
kfree(inq_response);
- out_mem:
return res;
}
@@ -965,7 +755,7 @@ static int nvme_trans_bdev_char_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
u8 *inq_response;
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int xfer_len;
inq_response = kzalloc(EXTENDED_INQUIRY_DATA_PAGE_LENGTH, GFP_KERNEL);
@@ -994,7 +784,7 @@ static int nvme_trans_bdev_char_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_log_supp_pages(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int xfer_len;
u8 *log_response;
@@ -1022,47 +812,30 @@ static int nvme_trans_log_supp_pages(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_log_info_exceptions(struct nvme_ns *ns,
struct sg_io_hdr *hdr, int alloc_len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int xfer_len;
u8 *log_response;
- struct nvme_command c;
struct nvme_dev *dev = ns->dev;
struct nvme_smart_log *smart_log;
- dma_addr_t dma_addr;
- void *mem;
u8 temp_c;
u16 temp_k;
log_response = kzalloc(LOG_INFO_EXCP_PAGE_LENGTH, GFP_KERNEL);
- if (log_response == NULL) {
- res = -ENOMEM;
- goto out_mem;
- }
+ if (log_response == NULL)
+ return -ENOMEM;
- mem = dma_alloc_coherent(&dev->pci_dev->dev,
- sizeof(struct nvme_smart_log),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out_dma;
- }
+ res = nvme_get_log_page(dev, &smart_log);
+ if (res < 0)
+ goto out_free_response;
- /* Get SMART Log Page */
- memset(&c, 0, sizeof(c));
- c.common.opcode = nvme_admin_get_log_page;
- c.common.nsid = cpu_to_le32(0xFFFFFFFF);
- c.common.prp1 = cpu_to_le64(dma_addr);
- c.common.cdw10[0] = cpu_to_le32((((sizeof(struct nvme_smart_log) /
- BYTES_TO_DWORDS) - 1) << 16) | NVME_LOG_SMART);
- res = nvme_submit_admin_cmd(dev, &c, NULL);
if (res != NVME_SC_SUCCESS) {
temp_c = LOG_TEMP_UNKNOWN;
} else {
- smart_log = mem;
temp_k = (smart_log->temperature[1] << 8) +
(smart_log->temperature[0]);
temp_c = temp_k - KELVIN_TEMP_FACTOR;
}
+ kfree(smart_log);
log_response[0] = LOG_PAGE_INFORMATIONAL_EXCEPTIONS_PAGE;
/* Subpage=0x00, Page Length MSB=0 */
@@ -1078,59 +851,39 @@ static int nvme_trans_log_info_exceptions(struct nvme_ns *ns,
xfer_len = min(alloc_len, LOG_INFO_EXCP_PAGE_LENGTH);
res = nvme_trans_copy_to_user(hdr, log_response, xfer_len);
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_smart_log),
- mem, dma_addr);
- out_dma:
+ out_free_response:
kfree(log_response);
- out_mem:
return res;
}
static int nvme_trans_log_temperature(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int xfer_len;
u8 *log_response;
- struct nvme_command c;
struct nvme_dev *dev = ns->dev;
struct nvme_smart_log *smart_log;
- dma_addr_t dma_addr;
- void *mem;
u32 feature_resp;
u8 temp_c_cur, temp_c_thresh;
u16 temp_k;
log_response = kzalloc(LOG_TEMP_PAGE_LENGTH, GFP_KERNEL);
- if (log_response == NULL) {
- res = -ENOMEM;
- goto out_mem;
- }
+ if (log_response == NULL)
+ return -ENOMEM;
- mem = dma_alloc_coherent(&dev->pci_dev->dev,
- sizeof(struct nvme_smart_log),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out_dma;
- }
+ res = nvme_get_log_page(dev, &smart_log);
+ if (res < 0)
+ goto out_free_response;
- /* Get SMART Log Page */
- memset(&c, 0, sizeof(c));
- c.common.opcode = nvme_admin_get_log_page;
- c.common.nsid = cpu_to_le32(0xFFFFFFFF);
- c.common.prp1 = cpu_to_le64(dma_addr);
- c.common.cdw10[0] = cpu_to_le32((((sizeof(struct nvme_smart_log) /
- BYTES_TO_DWORDS) - 1) << 16) | NVME_LOG_SMART);
- res = nvme_submit_admin_cmd(dev, &c, NULL);
if (res != NVME_SC_SUCCESS) {
temp_c_cur = LOG_TEMP_UNKNOWN;
} else {
- smart_log = mem;
temp_k = (smart_log->temperature[1] << 8) +
(smart_log->temperature[0]);
temp_c_cur = temp_k - KELVIN_TEMP_FACTOR;
}
+ kfree(smart_log);
/* Get Features for Temp Threshold */
res = nvme_get_features(dev, NVME_FEAT_TEMP_THRESH, 0, 0,
@@ -1159,11 +912,8 @@ static int nvme_trans_log_temperature(struct nvme_ns *ns, struct sg_io_hdr *hdr,
xfer_len = min(alloc_len, LOG_TEMP_PAGE_LENGTH);
res = nvme_trans_copy_to_user(hdr, log_response, xfer_len);
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_smart_log),
- mem, dma_addr);
- out_dma:
+ out_free_response:
kfree(log_response);
- out_mem:
return res;
}
@@ -1174,59 +924,45 @@ static int nvme_trans_fill_mode_parm_hdr(u8 *resp, int len, u8 cdb10, u8 llbaa,
{
/* Quick check to make sure I don't stomp on my own memory... */
if ((cdb10 && len < 8) || (!cdb10 && len < 4))
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
if (cdb10) {
resp[0] = (mode_data_length & 0xFF00) >> 8;
resp[1] = (mode_data_length & 0x00FF);
- /* resp[2] and [3] are zero */
+ resp[3] = 0x10 /* DPOFUA */;
resp[4] = llbaa;
resp[5] = RESERVED_FIELD;
resp[6] = (blk_desc_len & 0xFF00) >> 8;
resp[7] = (blk_desc_len & 0x00FF);
} else {
resp[0] = (mode_data_length & 0x00FF);
- /* resp[1] and [2] are zero */
+ resp[2] = 0x10 /* DPOFUA */;
resp[3] = (blk_desc_len & 0x00FF);
}
- return SNTI_TRANSLATION_SUCCESS;
+ return 0;
}
static int nvme_trans_fill_blk_desc(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *resp, int len, u8 llbaa)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ns *id_ns;
u8 flbas;
u32 lba_length;
if (llbaa == 0 && len < MODE_PAGE_BLK_DES_LEN)
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
else if (llbaa > 0 && len < MODE_PAGE_LLBAA_BLK_DES_LEN)
- return SNTI_INTERNAL_ERROR;
-
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
+ return -EINVAL;
- /* nvme ns identify */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ns = mem;
+ return res;
+
flbas = (id_ns->flbas) & 0x0F;
lba_length = (1 << (id_ns->lbaf[flbas].ds));
@@ -1246,10 +982,7 @@ static int nvme_trans_fill_blk_desc(struct nvme_ns *ns, struct sg_io_hdr *hdr,
memcpy(&resp[12], &tmp_len, sizeof(u32));
}
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out:
+ kfree(id_ns);
return res;
}
@@ -1258,7 +991,7 @@ static int nvme_trans_fill_control_page(struct nvme_ns *ns,
int len)
{
if (len < MODE_PAGE_CONTROL_LEN)
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
resp[0] = MODE_PAGE_CONTROL;
resp[1] = MODE_PAGE_CONTROL_LEN_FIELD;
@@ -1272,78 +1005,69 @@ static int nvme_trans_fill_control_page(struct nvme_ns *ns,
resp[9] = 0xFF;
/* Bytes 10,11: Extended selftest completion time = 0x0000 */
- return SNTI_TRANSLATION_SUCCESS;
+ return 0;
}
static int nvme_trans_fill_caching_page(struct nvme_ns *ns,
struct sg_io_hdr *hdr,
u8 *resp, int len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
u32 feature_resp;
u8 vwc;
if (len < MODE_PAGE_CACHING_LEN)
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
nvme_sc = nvme_get_features(dev, NVME_FEAT_VOLATILE_WC, 0, 0,
&feature_resp);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out;
- if (nvme_sc) {
- res = nvme_sc;
- goto out;
- }
+ return res;
+
vwc = feature_resp & 0x00000001;
resp[0] = MODE_PAGE_CACHING;
resp[1] = MODE_PAGE_CACHING_LEN_FIELD;
resp[2] = vwc << 2;
-
- out:
- return res;
+ return 0;
}
static int nvme_trans_fill_pow_cnd_page(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *resp,
int len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
-
if (len < MODE_PAGE_POW_CND_LEN)
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
resp[0] = MODE_PAGE_POWER_CONDITION;
resp[1] = MODE_PAGE_POW_CND_LEN_FIELD;
/* All other bytes are zero */
- return res;
+ return 0;
}
static int nvme_trans_fill_inf_exc_page(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *resp,
int len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
-
if (len < MODE_PAGE_INF_EXC_LEN)
- return SNTI_INTERNAL_ERROR;
+ return -EINVAL;
resp[0] = MODE_PAGE_INFO_EXCEP;
resp[1] = MODE_PAGE_INF_EXC_LEN_FIELD;
resp[2] = 0x88;
/* All other bytes are zero */
- return res;
+ return 0;
}
static int nvme_trans_fill_all_pages(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *resp, int len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u16 mode_pages_offset_1 = 0;
u16 mode_pages_offset_2, mode_pages_offset_3, mode_pages_offset_4;
@@ -1353,23 +1077,18 @@ static int nvme_trans_fill_all_pages(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = nvme_trans_fill_caching_page(ns, hdr, &resp[mode_pages_offset_1],
MODE_PAGE_CACHING_LEN);
- if (res != SNTI_TRANSLATION_SUCCESS)
- goto out;
+ if (res)
+ return res;
res = nvme_trans_fill_control_page(ns, hdr, &resp[mode_pages_offset_2],
MODE_PAGE_CONTROL_LEN);
- if (res != SNTI_TRANSLATION_SUCCESS)
- goto out;
+ if (res)
+ return res;
res = nvme_trans_fill_pow_cnd_page(ns, hdr, &resp[mode_pages_offset_3],
MODE_PAGE_POW_CND_LEN);
- if (res != SNTI_TRANSLATION_SUCCESS)
- goto out;
- res = nvme_trans_fill_inf_exc_page(ns, hdr, &resp[mode_pages_offset_4],
+ if (res)
+ return res;
+ return nvme_trans_fill_inf_exc_page(ns, hdr, &resp[mode_pages_offset_4],
MODE_PAGE_INF_EXC_LEN);
- if (res != SNTI_TRANSLATION_SUCCESS)
- goto out;
-
- out:
- return res;
}
static inline int nvme_trans_get_blk_desc_len(u8 dbd, u8 llbaa)
@@ -1390,7 +1109,7 @@ static int nvme_trans_mode_page_create(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *, int),
u16 mode_pages_tot_len)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int xfer_len;
u8 *response;
u8 dbd, llbaa;
@@ -1399,9 +1118,10 @@ static int nvme_trans_mode_page_create(struct nvme_ns *ns,
u16 mode_pages_offset_1;
u16 blk_desc_len, blk_desc_offset, mode_data_length;
- dbd = GET_MODE_SENSE_DBD(cmd);
- llbaa = GET_MODE_SENSE_LLBAA(cmd);
- mph_size = GET_MODE_SENSE_MPH_SIZE(cdb10);
+ dbd = (cmd[1] & MODE_SENSE_DBD_MASK) >> MODE_SENSE_DBD_SHIFT;
+ llbaa = (cmd[1] & MODE_SENSE_LLBAA_MASK) >> MODE_SENSE_LLBAA_SHIFT;
+ mph_size = cdb10 ? MODE_SENSE10_MPH_SIZE : MODE_SENSE6_MPH_SIZE;
+
blk_desc_len = nvme_trans_get_blk_desc_len(dbd, llbaa);
resp_size = mph_size + blk_desc_len + mode_pages_tot_len;
@@ -1419,18 +1139,18 @@ static int nvme_trans_mode_page_create(struct nvme_ns *ns,
res = nvme_trans_fill_mode_parm_hdr(&response[0], mph_size, cdb10,
llbaa, mode_data_length, blk_desc_len);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out_free;
if (blk_desc_len > 0) {
res = nvme_trans_fill_blk_desc(ns, hdr,
&response[blk_desc_offset],
blk_desc_len, llbaa);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out_free;
}
res = mode_page_fill_func(ns, hdr, &response[mode_pages_offset_1],
mode_pages_tot_len);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out_free;
xfer_len = min(alloc_len, resp_size);
@@ -1485,33 +1205,20 @@ static void nvme_trans_fill_read_cap(u8 *response, struct nvme_id_ns *id_ns,
static int nvme_trans_power_state(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 pc, u8 pcmod, u8 start)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ctrl *id_ctrl;
int lowest_pow_st; /* max npss = lowest power consumption */
unsigned ps_desired = 0;
- /* NVMe Controller Identify */
- mem = dma_alloc_coherent(&dev->pci_dev->dev,
- sizeof(struct nvme_id_ctrl),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
- nvme_sc = nvme_identify(dev, 0, 1, dma_addr);
+ nvme_sc = nvme_identify_ctrl(dev, &id_ctrl);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ctrl = mem;
+ return res;
+
lowest_pow_st = max(POWER_STATE_0, (int)(id_ctrl->npss - 1));
+ kfree(id_ctrl);
switch (pc) {
case NVME_POWER_STATE_START_VALID:
@@ -1551,79 +1258,48 @@ static int nvme_trans_power_state(struct nvme_ns *ns, struct sg_io_hdr *hdr,
}
nvme_sc = nvme_set_features(dev, NVME_FEAT_POWER_MGMT, ps_desired, 0,
NULL);
- res = nvme_trans_status_code(hdr, nvme_sc);
- if (res)
- goto out_dma;
- if (nvme_sc)
- res = nvme_sc;
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ctrl), mem,
- dma_addr);
- out:
- return res;
+ return nvme_trans_status_code(hdr, nvme_sc);
}
-/* Write Buffer Helper Functions */
-/* Also using this for Format Unit with hdr passed as NULL, and buffer_id, 0 */
+static int nvme_trans_send_activate_fw_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
+ u8 buffer_id)
+{
+ struct nvme_command c;
+ int nvme_sc;
-static int nvme_trans_send_fw_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
+ memset(&c, 0, sizeof(c));
+ c.common.opcode = nvme_admin_activate_fw;
+ c.common.cdw10[0] = cpu_to_le32(buffer_id | NVME_FWACT_REPL_ACTV);
+
+ nvme_sc = nvme_submit_sync_cmd(ns->queue, &c, NULL, 0);
+ return nvme_trans_status_code(hdr, nvme_sc);
+}
+
+static int nvme_trans_send_download_fw_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 opcode, u32 tot_len, u32 offset,
u8 buffer_id)
{
- int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
struct nvme_command c;
- struct nvme_iod *iod = NULL;
- unsigned length;
- memset(&c, 0, sizeof(c));
- c.common.opcode = opcode;
- if (opcode == nvme_admin_download_fw) {
- if (hdr->iovec_count > 0) {
- /* Assuming SGL is not allowed for this command */
- res = nvme_trans_completion(hdr,
- SAM_STAT_CHECK_CONDITION,
- ILLEGAL_REQUEST,
- SCSI_ASC_INVALID_CDB,
- SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- goto out;
- }
- iod = nvme_map_user_pages(dev, DMA_TO_DEVICE,
- (unsigned long)hdr->dxferp, tot_len);
- if (IS_ERR(iod)) {
- res = PTR_ERR(iod);
- goto out;
- }
- length = nvme_setup_prps(dev, iod, tot_len, GFP_KERNEL);
- if (length != tot_len) {
- res = -ENOMEM;
- goto out_unmap;
- }
-
- c.dlfw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
- c.dlfw.prp2 = cpu_to_le64(iod->first_dma);
- c.dlfw.numd = cpu_to_le32((tot_len/BYTES_TO_DWORDS) - 1);
- c.dlfw.offset = cpu_to_le32(offset/BYTES_TO_DWORDS);
- } else if (opcode == nvme_admin_activate_fw) {
- u32 cdw10 = buffer_id | NVME_FWACT_REPL_ACTV;
- c.common.cdw10[0] = cpu_to_le32(cdw10);
+ if (hdr->iovec_count > 0) {
+ /* Assuming SGL is not allowed for this command */
+ return nvme_trans_completion(hdr,
+ SAM_STAT_CHECK_CONDITION,
+ ILLEGAL_REQUEST,
+ SCSI_ASC_INVALID_CDB,
+ SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
}
- nvme_sc = nvme_submit_admin_cmd(dev, &c, NULL);
- res = nvme_trans_status_code(hdr, nvme_sc);
- if (res)
- goto out_unmap;
- if (nvme_sc)
- res = nvme_sc;
-
- out_unmap:
- if (opcode == nvme_admin_download_fw) {
- nvme_unmap_user_pages(dev, DMA_TO_DEVICE, iod);
- nvme_free_iod(dev, iod);
- }
- out:
- return res;
+ memset(&c, 0, sizeof(c));
+ c.common.opcode = nvme_admin_download_fw;
+ c.dlfw.numd = cpu_to_le32((tot_len/BYTES_TO_DWORDS) - 1);
+ c.dlfw.offset = cpu_to_le32(offset/BYTES_TO_DWORDS);
+
+ nvme_sc = __nvme_submit_sync_cmd(dev->admin_q, &c, NULL,
+ hdr->dxferp, tot_len, NULL, 0);
+ return nvme_trans_status_code(hdr, nvme_sc);
}
/* Mode Select Helper Functions */
@@ -1686,7 +1362,7 @@ static void nvme_trans_modesel_save_bd(struct nvme_ns *ns, u8 *parm_list,
static int nvme_trans_modesel_get_mp(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *mode_page, u8 page_code)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
unsigned dword11;
@@ -1697,12 +1373,6 @@ static int nvme_trans_modesel_get_mp(struct nvme_ns *ns, struct sg_io_hdr *hdr,
nvme_sc = nvme_set_features(dev, NVME_FEAT_VOLATILE_WC, dword11,
0, NULL);
res = nvme_trans_status_code(hdr, nvme_sc);
- if (res)
- break;
- if (nvme_sc) {
- res = nvme_sc;
- break;
- }
break;
case MODE_PAGE_CONTROL:
break;
@@ -1714,8 +1384,6 @@ static int nvme_trans_modesel_get_mp(struct nvme_ns *ns, struct sg_io_hdr *hdr,
ILLEGAL_REQUEST,
SCSI_ASC_INVALID_PARAMETER,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- if (!res)
- res = SNTI_INTERNAL_ERROR;
break;
}
break;
@@ -1723,8 +1391,6 @@ static int nvme_trans_modesel_get_mp(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- if (!res)
- res = SNTI_INTERNAL_ERROR;
break;
}
@@ -1735,7 +1401,7 @@ static int nvme_trans_modesel_data(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd, u16 parm_list_len, u8 pf,
u8 sp, u8 cdb10)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u8 *parm_list;
u16 bd_len;
u8 llbaa = 0;
@@ -1751,7 +1417,7 @@ static int nvme_trans_modesel_data(struct nvme_ns *ns, struct sg_io_hdr *hdr,
}
res = nvme_trans_copy_from_user(hdr, parm_list, parm_list_len);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out_mem;
nvme_trans_modesel_get_bd_len(parm_list, cdb10, &bd_len, &llbaa);
@@ -1789,7 +1455,7 @@ static int nvme_trans_modesel_data(struct nvme_ns *ns, struct sg_io_hdr *hdr,
mp_size = parm_list[index + 1] + 2;
res = nvme_trans_modesel_get_mp(ns, hdr, &parm_list[index],
page_code);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
break;
index += mp_size;
} while (index < parm_list_len);
@@ -1805,12 +1471,9 @@ static int nvme_trans_modesel_data(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_fmt_set_blk_size_count(struct nvme_ns *ns,
struct sg_io_hdr *hdr)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
- struct nvme_id_ns *id_ns;
u8 flbas;
/*
@@ -1821,22 +1484,12 @@ static int nvme_trans_fmt_set_blk_size_count(struct nvme_ns *ns,
*/
if (ns->mode_select_num_blocks == 0 || ns->mode_select_block_len == 0) {
- mem = dma_alloc_coherent(&dev->pci_dev->dev,
- sizeof(struct nvme_id_ns), &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
- /* nvme ns identify */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ struct nvme_id_ns *id_ns;
+
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ns = mem;
+ return res;
if (ns->mode_select_num_blocks == 0)
ns->mode_select_num_blocks = le64_to_cpu(id_ns->ncap);
@@ -1845,18 +1498,17 @@ static int nvme_trans_fmt_set_blk_size_count(struct nvme_ns *ns,
ns->mode_select_block_len =
(1 << (id_ns->lbaf[flbas].ds));
}
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- mem, dma_addr);
+
+ kfree(id_ns);
}
- out:
- return res;
+
+ return 0;
}
static int nvme_trans_fmt_get_parm_header(struct sg_io_hdr *hdr, u8 len,
u8 format_prot_info, u8 *nvme_pf_code)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u8 *parm_list;
u8 pf_usage, pf_code;
@@ -1866,7 +1518,7 @@ static int nvme_trans_fmt_get_parm_header(struct sg_io_hdr *hdr, u8 len,
goto out;
}
res = nvme_trans_copy_from_user(hdr, parm_list, len);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out_mem;
if ((parm_list[FORMAT_UNIT_IMMED_OFFSET] &
@@ -1916,11 +1568,9 @@ static int nvme_trans_fmt_get_parm_header(struct sg_io_hdr *hdr, u8 len,
static int nvme_trans_fmt_send_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 prot_info)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ns *id_ns;
u8 i;
u8 flbas, nlbaf;
@@ -1929,22 +1579,11 @@ static int nvme_trans_fmt_send_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
struct nvme_command c;
/* Loop thru LBAF's in id_ns to match reqd lbaf, put in cdw10 */
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
- /* nvme ns identify */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ns = mem;
+ return res;
+
flbas = (id_ns->flbas) & 0x0F;
nlbaf = id_ns->nlbaf;
@@ -1972,69 +1611,13 @@ static int nvme_trans_fmt_send_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
c.format.nsid = cpu_to_le32(ns->ns_id);
c.format.cdw10 = cpu_to_le32(cdw10);
- nvme_sc = nvme_submit_admin_cmd(dev, &c, NULL);
+ nvme_sc = nvme_submit_sync_cmd(dev->admin_q, &c, NULL, 0);
res = nvme_trans_status_code(hdr, nvme_sc);
- if (res)
- goto out_dma;
- if (nvme_sc)
- res = nvme_sc;
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out:
+ kfree(id_ns);
return res;
}
-/* Read/Write Helper Functions */
-
-static inline void nvme_trans_get_io_cdb6(u8 *cmd,
- struct nvme_trans_io_cdb *cdb_info)
-{
- cdb_info->fua = 0;
- cdb_info->prot_info = 0;
- cdb_info->lba = GET_U32_FROM_CDB(cmd, IO_6_CDB_LBA_OFFSET) &
- IO_6_CDB_LBA_MASK;
- cdb_info->xfer_len = GET_U8_FROM_CDB(cmd, IO_6_CDB_TX_LEN_OFFSET);
-
- /* sbc3r27 sec 5.32 - TRANSFER LEN of 0 implies a 256 Block transfer */
- if (cdb_info->xfer_len == 0)
- cdb_info->xfer_len = IO_6_DEFAULT_TX_LEN;
-}
-
-static inline void nvme_trans_get_io_cdb10(u8 *cmd,
- struct nvme_trans_io_cdb *cdb_info)
-{
- cdb_info->fua = GET_U8_FROM_CDB(cmd, IO_10_CDB_FUA_OFFSET) &
- IO_CDB_FUA_MASK;
- cdb_info->prot_info = GET_U8_FROM_CDB(cmd, IO_10_CDB_WP_OFFSET) &
- IO_CDB_WP_MASK >> IO_CDB_WP_SHIFT;
- cdb_info->lba = GET_U32_FROM_CDB(cmd, IO_10_CDB_LBA_OFFSET);
- cdb_info->xfer_len = GET_U16_FROM_CDB(cmd, IO_10_CDB_TX_LEN_OFFSET);
-}
-
-static inline void nvme_trans_get_io_cdb12(u8 *cmd,
- struct nvme_trans_io_cdb *cdb_info)
-{
- cdb_info->fua = GET_U8_FROM_CDB(cmd, IO_12_CDB_FUA_OFFSET) &
- IO_CDB_FUA_MASK;
- cdb_info->prot_info = GET_U8_FROM_CDB(cmd, IO_12_CDB_WP_OFFSET) &
- IO_CDB_WP_MASK >> IO_CDB_WP_SHIFT;
- cdb_info->lba = GET_U32_FROM_CDB(cmd, IO_12_CDB_LBA_OFFSET);
- cdb_info->xfer_len = GET_U32_FROM_CDB(cmd, IO_12_CDB_TX_LEN_OFFSET);
-}
-
-static inline void nvme_trans_get_io_cdb16(u8 *cmd,
- struct nvme_trans_io_cdb *cdb_info)
-{
- cdb_info->fua = GET_U8_FROM_CDB(cmd, IO_16_CDB_FUA_OFFSET) &
- IO_CDB_FUA_MASK;
- cdb_info->prot_info = GET_U8_FROM_CDB(cmd, IO_16_CDB_WP_OFFSET) &
- IO_CDB_WP_MASK >> IO_CDB_WP_SHIFT;
- cdb_info->lba = GET_U64_FROM_CDB(cmd, IO_16_CDB_LBA_OFFSET);
- cdb_info->xfer_len = GET_U32_FROM_CDB(cmd, IO_16_CDB_TX_LEN_OFFSET);
-}
-
static inline u32 nvme_trans_io_get_num_cmds(struct sg_io_hdr *hdr,
struct nvme_trans_io_cdb *cdb_info,
u32 max_blocks)
@@ -2064,11 +1647,8 @@ static u16 nvme_trans_io_get_control(struct nvme_ns *ns,
static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
struct nvme_trans_io_cdb *cdb_info, u8 is_write)
{
- int res = SNTI_TRANSLATION_SUCCESS;
- int nvme_sc;
- struct nvme_dev *dev = ns->dev;
+ int nvme_sc = NVME_SC_SUCCESS;
u32 num_cmds;
- struct nvme_iod *iod;
u64 unit_len;
u64 unit_num_blocks; /* Number of blocks to xfer in each nvme cmd */
u32 retcode;
@@ -2119,45 +1699,20 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
control = nvme_trans_io_get_control(ns, cdb_info);
c.rw.control = cpu_to_le16(control);
- iod = nvme_map_user_pages(dev,
- (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
- (unsigned long)next_mapping_addr, unit_len);
- if (IS_ERR(iod)) {
- res = PTR_ERR(iod);
- goto out;
- }
- retcode = nvme_setup_prps(dev, iod, unit_len, GFP_KERNEL);
- if (retcode != unit_len) {
- nvme_unmap_user_pages(dev,
- (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
- iod);
- nvme_free_iod(dev, iod);
- res = -ENOMEM;
- goto out;
+ if (get_capacity(ns->disk) - unit_num_blocks <
+ cdb_info->lba + nvme_offset) {
+ nvme_sc = NVME_SC_LBA_RANGE;
+ break;
}
- c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
- c.rw.prp2 = cpu_to_le64(iod->first_dma);
+ nvme_sc = __nvme_submit_sync_cmd(ns->queue, &c, NULL,
+ next_mapping_addr, unit_len, NULL, 0);
+ if (nvme_sc)
+ break;
nvme_offset += unit_num_blocks;
-
- nvme_sc = nvme_submit_io_cmd(dev, ns, &c, NULL);
- if (nvme_sc != NVME_SC_SUCCESS) {
- nvme_unmap_user_pages(dev,
- (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
- iod);
- nvme_free_iod(dev, iod);
- res = nvme_trans_status_code(hdr, nvme_sc);
- goto out;
- }
- nvme_unmap_user_pages(dev,
- (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
- iod);
- nvme_free_iod(dev, iod);
}
- res = nvme_trans_status_code(hdr, NVME_SC_SUCCESS);
- out:
- return res;
+ return nvme_trans_status_code(hdr, nvme_sc);
}
@@ -2166,8 +1721,8 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, u8 is_write,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
- struct nvme_trans_io_cdb cdb_info;
+ int res = 0;
+ struct nvme_trans_io_cdb cdb_info = { 0, };
u8 opcode = cmd[0];
u64 xfer_bytes;
u64 sum_iov_len = 0;
@@ -2175,27 +1730,52 @@ static int nvme_trans_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, u8 is_write,
int i;
size_t not_copied;
- /* Extract Fields from CDB */
+ /*
+ * The FUA and WPROTECT fields are not supported in 6-byte CDBs,
+ * but always in the same place for all others.
+ */
+ switch (opcode) {
+ case WRITE_6:
+ case READ_6:
+ break;
+ default:
+ cdb_info.fua = cmd[1] & 0x8;
+ cdb_info.prot_info = (cmd[1] & 0xe0) >> 5;
+ if (cdb_info.prot_info && !ns->pi_type) {
+ return nvme_trans_completion(hdr,
+ SAM_STAT_CHECK_CONDITION,
+ ILLEGAL_REQUEST,
+ SCSI_ASC_INVALID_CDB,
+ SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+ }
+ }
+
switch (opcode) {
case WRITE_6:
case READ_6:
- nvme_trans_get_io_cdb6(cmd, &cdb_info);
+ cdb_info.lba = get_unaligned_be24(&cmd[1]);
+ cdb_info.xfer_len = cmd[4];
+ if (cdb_info.xfer_len == 0)
+ cdb_info.xfer_len = 256;
break;
case WRITE_10:
case READ_10:
- nvme_trans_get_io_cdb10(cmd, &cdb_info);
+ cdb_info.lba = get_unaligned_be32(&cmd[2]);
+ cdb_info.xfer_len = get_unaligned_be16(&cmd[7]);
break;
case WRITE_12:
case READ_12:
- nvme_trans_get_io_cdb12(cmd, &cdb_info);
+ cdb_info.lba = get_unaligned_be32(&cmd[2]);
+ cdb_info.xfer_len = get_unaligned_be32(&cmd[6]);
break;
case WRITE_16:
case READ_16:
- nvme_trans_get_io_cdb16(cmd, &cdb_info);
+ cdb_info.lba = get_unaligned_be64(&cmd[2]);
+ cdb_info.xfer_len = get_unaligned_be32(&cmd[10]);
break;
default:
/* Will never really reach here */
- res = SNTI_INTERNAL_ERROR;
+ res = -EIO;
goto out;
}
@@ -2237,7 +1817,7 @@ static int nvme_trans_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, u8 is_write,
/* Send NVMe IO Command(s) */
res = nvme_trans_do_nvme_io(ns, hdr, &cdb_info, is_write);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out;
out:
@@ -2247,15 +1827,15 @@ static int nvme_trans_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, u8 is_write,
static int nvme_trans_inquiry(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
u8 evpd;
u8 page_code;
int alloc_len;
u8 *inq_response;
- evpd = GET_INQ_EVPD_BIT(cmd);
- page_code = GET_INQ_PAGE_CODE(cmd);
- alloc_len = GET_INQ_ALLOC_LENGTH(cmd);
+ evpd = cmd[1] & 0x01;
+ page_code = cmd[2];
+ alloc_len = get_unaligned_be16(&cmd[3]);
inq_response = kmalloc(max(alloc_len, STANDARD_INQUIRY_LENGTH),
GFP_KERNEL);
@@ -2316,29 +1896,27 @@ static int nvme_trans_inquiry(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_log_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u16 alloc_len;
- u8 sp;
u8 pc;
u8 page_code;
- sp = GET_U8_FROM_CDB(cmd, LOG_SENSE_CDB_SP_OFFSET);
- if (sp != LOG_SENSE_CDB_SP_NOT_ENABLED) {
+ if (cmd[1] != LOG_SENSE_CDB_SP_NOT_ENABLED) {
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out;
}
- pc = GET_U8_FROM_CDB(cmd, LOG_SENSE_CDB_PC_OFFSET);
- page_code = pc & LOG_SENSE_CDB_PAGE_CODE_MASK;
- pc = (pc & LOG_SENSE_CDB_PC_MASK) >> LOG_SENSE_CDB_PC_SHIFT;
+
+ page_code = cmd[2] & LOG_SENSE_CDB_PAGE_CODE_MASK;
+ pc = (cmd[2] & LOG_SENSE_CDB_PC_MASK) >> LOG_SENSE_CDB_PC_SHIFT;
if (pc != LOG_SENSE_CDB_PC_CUMULATIVE_VALUES) {
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out;
}
- alloc_len = GET_U16_FROM_CDB(cmd, LOG_SENSE_CDB_ALLOC_LENGTH_OFFSET);
+ alloc_len = get_unaligned_be16(&cmd[7]);
switch (page_code) {
case LOG_PAGE_SUPPORTED_LOG_PAGES_PAGE:
res = nvme_trans_log_supp_pages(ns, hdr, alloc_len);
@@ -2363,24 +1941,18 @@ static int nvme_trans_log_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_mode_select(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
u8 cdb10 = 0;
u16 parm_list_len;
u8 page_format;
u8 save_pages;
- page_format = GET_U8_FROM_CDB(cmd, MODE_SELECT_CDB_PAGE_FORMAT_OFFSET);
- page_format &= MODE_SELECT_CDB_PAGE_FORMAT_MASK;
+ page_format = cmd[1] & MODE_SELECT_CDB_PAGE_FORMAT_MASK;
+ save_pages = cmd[1] & MODE_SELECT_CDB_SAVE_PAGES_MASK;
- save_pages = GET_U8_FROM_CDB(cmd, MODE_SELECT_CDB_SAVE_PAGES_OFFSET);
- save_pages &= MODE_SELECT_CDB_SAVE_PAGES_MASK;
-
- if (GET_OPCODE(cmd) == MODE_SELECT) {
- parm_list_len = GET_U8_FROM_CDB(cmd,
- MODE_SELECT_6_CDB_PARAM_LIST_LENGTH_OFFSET);
+ if (cmd[0] == MODE_SELECT) {
+ parm_list_len = cmd[4];
} else {
- parm_list_len = GET_U16_FROM_CDB(cmd,
- MODE_SELECT_10_CDB_PARAM_LIST_LENGTH_OFFSET);
+ parm_list_len = cmd[7];
cdb10 = 1;
}
@@ -2389,42 +1961,36 @@ static int nvme_trans_mode_select(struct nvme_ns *ns, struct sg_io_hdr *hdr,
* According to SPC-4 r24, a paramter list length field of 0
* shall not be considered an error
*/
- res = nvme_trans_modesel_data(ns, hdr, cmd, parm_list_len,
+ return nvme_trans_modesel_data(ns, hdr, cmd, parm_list_len,
page_format, save_pages, cdb10);
}
- return res;
+ return 0;
}
static int nvme_trans_mode_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
u16 alloc_len;
u8 cdb10 = 0;
- u8 page_code;
- u8 pc;
- if (GET_OPCODE(cmd) == MODE_SENSE) {
- alloc_len = GET_U8_FROM_CDB(cmd, MODE_SENSE6_ALLOC_LEN_OFFSET);
+ if (cmd[0] == MODE_SENSE) {
+ alloc_len = cmd[4];
} else {
- alloc_len = GET_U16_FROM_CDB(cmd,
- MODE_SENSE10_ALLOC_LEN_OFFSET);
+ alloc_len = get_unaligned_be16(&cmd[7]);
cdb10 = 1;
}
- pc = GET_U8_FROM_CDB(cmd, MODE_SENSE_PAGE_CONTROL_OFFSET) &
- MODE_SENSE_PAGE_CONTROL_MASK;
- if (pc != MODE_SENSE_PC_CURRENT_VALUES) {
+ if ((cmd[2] & MODE_SENSE_PAGE_CONTROL_MASK) !=
+ MODE_SENSE_PC_CURRENT_VALUES) {
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out;
}
- page_code = GET_U8_FROM_CDB(cmd, MODE_SENSE_PAGE_CODE_OFFSET) &
- MODE_SENSE_PAGE_CODE_MASK;
- switch (page_code) {
+ switch (cmd[2] & MODE_SENSE_PAGE_CODE_MASK) {
case MODE_PAGE_CACHING:
res = nvme_trans_mode_page_create(ns, hdr, cmd, alloc_len,
cdb10,
@@ -2467,47 +2033,34 @@ static int nvme_trans_mode_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
}
static int nvme_trans_read_capacity(struct nvme_ns *ns, struct sg_io_hdr *hdr,
- u8 *cmd)
+ u8 *cmd, u8 cdb16)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
- u32 alloc_len = READ_CAP_10_RESP_SIZE;
- u32 resp_size = READ_CAP_10_RESP_SIZE;
+ u32 alloc_len;
+ u32 resp_size;
u32 xfer_len;
- u8 cdb16;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ns *id_ns;
u8 *response;
- cdb16 = IS_READ_CAP_16(cmd);
if (cdb16) {
- alloc_len = GET_READ_CAP_16_ALLOC_LENGTH(cmd);
+ alloc_len = get_unaligned_be32(&cmd[10]);
resp_size = READ_CAP_16_RESP_SIZE;
+ } else {
+ alloc_len = READ_CAP_10_RESP_SIZE;
+ resp_size = READ_CAP_10_RESP_SIZE;
}
- mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
- /* nvme ns identify */
- nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+ nvme_sc = nvme_identify_ns(dev, ns->ns_id, &id_ns);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ns = mem;
+ return res;
response = kzalloc(resp_size, GFP_KERNEL);
if (response == NULL) {
res = -ENOMEM;
- goto out_dma;
+ goto out_free_id;
}
nvme_trans_fill_read_cap(response, id_ns, cdb16);
@@ -2515,72 +2068,53 @@ static int nvme_trans_read_capacity(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = nvme_trans_copy_to_user(hdr, response, xfer_len);
kfree(response);
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
- dma_addr);
- out:
+ out_free_id:
+ kfree(id_ns);
return res;
}
static int nvme_trans_report_luns(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
int nvme_sc;
u32 alloc_len, xfer_len, resp_size;
- u8 select_report;
u8 *response;
struct nvme_dev *dev = ns->dev;
- dma_addr_t dma_addr;
- void *mem;
struct nvme_id_ctrl *id_ctrl;
u32 ll_length, lun_id;
u8 lun_id_offset = REPORT_LUNS_FIRST_LUN_OFFSET;
__be32 tmp_len;
- alloc_len = GET_REPORT_LUNS_ALLOC_LENGTH(cmd);
- select_report = GET_U8_FROM_CDB(cmd, REPORT_LUNS_SR_OFFSET);
-
- if ((select_report != ALL_LUNS_RETURNED) &&
- (select_report != ALL_WELL_KNOWN_LUNS_RETURNED) &&
- (select_report != RESTRICTED_LUNS_RETURNED)) {
- res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
+ switch (cmd[2]) {
+ default:
+ return nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- goto out;
- } else {
- /* NVMe Controller Identify */
- mem = dma_alloc_coherent(&dev->pci_dev->dev,
- sizeof(struct nvme_id_ctrl),
- &dma_addr, GFP_KERNEL);
- if (mem == NULL) {
- res = -ENOMEM;
- goto out;
- }
- nvme_sc = nvme_identify(dev, 0, 1, dma_addr);
+ case ALL_LUNS_RETURNED:
+ case ALL_WELL_KNOWN_LUNS_RETURNED:
+ case RESTRICTED_LUNS_RETURNED:
+ nvme_sc = nvme_identify_ctrl(dev, &id_ctrl);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
- goto out_dma;
- if (nvme_sc) {
- res = nvme_sc;
- goto out_dma;
- }
- id_ctrl = mem;
+ return res;
+
ll_length = le32_to_cpu(id_ctrl->nn) * LUN_ENTRY_SIZE;
resp_size = ll_length + LUN_DATA_HEADER_SIZE;
+ alloc_len = get_unaligned_be32(&cmd[6]);
if (alloc_len < resp_size) {
res = nvme_trans_completion(hdr,
SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
- goto out_dma;
+ goto out_free_id;
}
response = kzalloc(resp_size, GFP_KERNEL);
if (response == NULL) {
res = -ENOMEM;
- goto out_dma;
+ goto out_free_id;
}
/* The first LUN ID will always be 0 per the SAM spec */
@@ -2601,24 +2135,21 @@ static int nvme_trans_report_luns(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = nvme_trans_copy_to_user(hdr, response, xfer_len);
kfree(response);
- out_dma:
- dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ctrl), mem,
- dma_addr);
- out:
+ out_free_id:
+ kfree(id_ctrl);
return res;
}
static int nvme_trans_request_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u8 alloc_len, xfer_len, resp_size;
u8 desc_format;
u8 *response;
- alloc_len = GET_REQUEST_SENSE_ALLOC_LENGTH(cmd);
- desc_format = GET_U8_FROM_CDB(cmd, REQUEST_SENSE_DESC_OFFSET);
- desc_format &= REQUEST_SENSE_DESC_MASK;
+ desc_format = cmd[1] & 0x01;
+ alloc_len = cmd[4];
resp_size = ((desc_format) ? (DESC_FMT_SENSE_DATA_SIZE) :
(FIXED_FMT_SENSE_DATA_SIZE));
@@ -2628,7 +2159,7 @@ static int nvme_trans_request_sense(struct nvme_ns *ns, struct sg_io_hdr *hdr,
goto out;
}
- if (desc_format == DESCRIPTOR_FORMAT_SENSE_DATA_TYPE) {
+ if (desc_format) {
/* Descriptor Format Sense Data */
response[0] = DESC_FORMAT_SENSE_DATA;
response[1] = NO_SENSE;
@@ -2667,95 +2198,58 @@ static int nvme_trans_security_protocol(struct nvme_ns *ns,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
}
-static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr,
- u8 *cmd)
+static int nvme_trans_synchronize_cache(struct nvme_ns *ns,
+ struct sg_io_hdr *hdr)
{
- int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
struct nvme_command c;
- u8 immed, pcmod, pc, no_flush, start;
- immed = GET_U8_FROM_CDB(cmd, START_STOP_UNIT_CDB_IMMED_OFFSET);
- pcmod = GET_U8_FROM_CDB(cmd, START_STOP_UNIT_CDB_POWER_COND_MOD_OFFSET);
- pc = GET_U8_FROM_CDB(cmd, START_STOP_UNIT_CDB_POWER_COND_OFFSET);
- no_flush = GET_U8_FROM_CDB(cmd, START_STOP_UNIT_CDB_NO_FLUSH_OFFSET);
- start = GET_U8_FROM_CDB(cmd, START_STOP_UNIT_CDB_START_OFFSET);
+ memset(&c, 0, sizeof(c));
+ c.common.opcode = nvme_cmd_flush;
+ c.common.nsid = cpu_to_le32(ns->ns_id);
- immed &= START_STOP_UNIT_CDB_IMMED_MASK;
- pcmod &= START_STOP_UNIT_CDB_POWER_COND_MOD_MASK;
- pc = (pc & START_STOP_UNIT_CDB_POWER_COND_MASK) >> NIBBLE_SHIFT;
- no_flush &= START_STOP_UNIT_CDB_NO_FLUSH_MASK;
- start &= START_STOP_UNIT_CDB_START_MASK;
+ nvme_sc = nvme_submit_sync_cmd(ns->queue, &c, NULL, 0);
+ return nvme_trans_status_code(hdr, nvme_sc);
+}
+
+static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr,
+ u8 *cmd)
+{
+ u8 immed, pcmod, pc, no_flush, start;
+
+ immed = cmd[1] & 0x01;
+ pcmod = cmd[3] & 0x0f;
+ pc = (cmd[4] & 0xf0) >> 4;
+ no_flush = cmd[4] & 0x04;
+ start = cmd[4] & 0x01;
if (immed != 0) {
- res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
+ return nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
} else {
if (no_flush == 0) {
/* Issue NVME FLUSH command prior to START STOP UNIT */
- memset(&c, 0, sizeof(c));
- c.common.opcode = nvme_cmd_flush;
- c.common.nsid = cpu_to_le32(ns->ns_id);
-
- nvme_sc = nvme_submit_io_cmd(ns->dev, ns, &c, NULL);
- res = nvme_trans_status_code(hdr, nvme_sc);
+ int res = nvme_trans_synchronize_cache(ns, hdr);
if (res)
- goto out;
- if (nvme_sc) {
- res = nvme_sc;
- goto out;
- }
+ return res;
}
/* Setup the expected power state transition */
- res = nvme_trans_power_state(ns, hdr, pc, pcmod, start);
+ return nvme_trans_power_state(ns, hdr, pc, pcmod, start);
}
-
- out:
- return res;
-}
-
-static int nvme_trans_synchronize_cache(struct nvme_ns *ns,
- struct sg_io_hdr *hdr, u8 *cmd)
-{
- int res = SNTI_TRANSLATION_SUCCESS;
- int nvme_sc;
- struct nvme_command c;
-
- memset(&c, 0, sizeof(c));
- c.common.opcode = nvme_cmd_flush;
- c.common.nsid = cpu_to_le32(ns->ns_id);
-
- nvme_sc = nvme_submit_io_cmd(ns->dev, ns, &c, NULL);
-
- res = nvme_trans_status_code(hdr, nvme_sc);
- if (res)
- goto out;
- if (nvme_sc)
- res = nvme_sc;
-
- out:
- return res;
}
static int nvme_trans_format_unit(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res;
u8 parm_hdr_len = 0;
u8 nvme_pf_code = 0;
u8 format_prot_info, long_list, format_data;
- format_prot_info = GET_U8_FROM_CDB(cmd,
- FORMAT_UNIT_CDB_FORMAT_PROT_INFO_OFFSET);
- long_list = GET_U8_FROM_CDB(cmd, FORMAT_UNIT_CDB_LONG_LIST_OFFSET);
- format_data = GET_U8_FROM_CDB(cmd, FORMAT_UNIT_CDB_FORMAT_DATA_OFFSET);
-
- format_prot_info = (format_prot_info &
- FORMAT_UNIT_CDB_FORMAT_PROT_INFO_MASK) >>
- FORMAT_UNIT_CDB_FORMAT_PROT_INFO_SHIFT;
- long_list &= FORMAT_UNIT_CDB_LONG_LIST_MASK;
- format_data &= FORMAT_UNIT_CDB_FORMAT_DATA_MASK;
+ format_prot_info = (cmd[1] & 0xc0) >> 6;
+ long_list = cmd[1] & 0x20;
+ format_data = cmd[1] & 0x10;
if (format_data != 0) {
if (format_prot_info != 0) {
@@ -2779,16 +2273,16 @@ static int nvme_trans_format_unit(struct nvme_ns *ns, struct sg_io_hdr *hdr,
if (parm_hdr_len > 0) {
res = nvme_trans_fmt_get_parm_header(hdr, parm_hdr_len,
format_prot_info, &nvme_pf_code);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out;
}
/* Attempt to activate any previously downloaded firmware image */
- res = nvme_trans_send_fw_cmd(ns, hdr, nvme_admin_activate_fw, 0, 0, 0);
+ res = nvme_trans_send_activate_fw_cmd(ns, hdr, 0);
/* Determine Block size and count and send format command */
res = nvme_trans_fmt_set_blk_size_count(ns, hdr);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out;
res = nvme_trans_fmt_send_cmd(ns, hdr, nvme_pf_code);
@@ -2801,28 +2295,24 @@ static int nvme_trans_test_unit_ready(struct nvme_ns *ns,
struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
struct nvme_dev *dev = ns->dev;
if (!(readl(&dev->bar->csts) & NVME_CSTS_RDY))
- res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
+ return nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
NOT_READY, SCSI_ASC_LUN_NOT_READY,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
else
- res = nvme_trans_completion(hdr, SAM_STAT_GOOD, NO_SENSE, 0, 0);
-
- return res;
+ return nvme_trans_completion(hdr, SAM_STAT_GOOD, NO_SENSE, 0, 0);
}
static int nvme_trans_write_buffer(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- int res = SNTI_TRANSLATION_SUCCESS;
+ int res = 0;
u32 buffer_offset, parm_list_length;
u8 buffer_id, mode;
- parm_list_length =
- GET_U24_FROM_CDB(cmd, WRITE_BUFFER_CDB_PARM_LIST_LENGTH_OFFSET);
+ parm_list_length = get_unaligned_be24(&cmd[6]);
if (parm_list_length % BYTES_TO_DWORDS != 0) {
/* NVMe expects Firmware file to be a whole number of DWORDS */
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
@@ -2830,38 +2320,32 @@ static int nvme_trans_write_buffer(struct nvme_ns *ns, struct sg_io_hdr *hdr,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out;
}
- buffer_id = GET_U8_FROM_CDB(cmd, WRITE_BUFFER_CDB_BUFFER_ID_OFFSET);
+ buffer_id = cmd[2];
if (buffer_id > NVME_MAX_FIRMWARE_SLOT) {
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out;
}
- mode = GET_U8_FROM_CDB(cmd, WRITE_BUFFER_CDB_MODE_OFFSET) &
- WRITE_BUFFER_CDB_MODE_MASK;
- buffer_offset =
- GET_U24_FROM_CDB(cmd, WRITE_BUFFER_CDB_BUFFER_OFFSET_OFFSET);
+ mode = cmd[1] & 0x1f;
+ buffer_offset = get_unaligned_be24(&cmd[3]);
switch (mode) {
case DOWNLOAD_SAVE_ACTIVATE:
- res = nvme_trans_send_fw_cmd(ns, hdr, nvme_admin_download_fw,
+ res = nvme_trans_send_download_fw_cmd(ns, hdr, nvme_admin_download_fw,
parm_list_length, buffer_offset,
buffer_id);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out;
- res = nvme_trans_send_fw_cmd(ns, hdr, nvme_admin_activate_fw,
- parm_list_length, buffer_offset,
- buffer_id);
+ res = nvme_trans_send_activate_fw_cmd(ns, hdr, buffer_id);
break;
case DOWNLOAD_SAVE_DEFER_ACTIVATE:
- res = nvme_trans_send_fw_cmd(ns, hdr, nvme_admin_download_fw,
+ res = nvme_trans_send_download_fw_cmd(ns, hdr, nvme_admin_download_fw,
parm_list_length, buffer_offset,
buffer_id);
break;
case ACTIVATE_DEFERRED_MICROCODE:
- res = nvme_trans_send_fw_cmd(ns, hdr, nvme_admin_activate_fw,
- parm_list_length, buffer_offset,
- buffer_id);
+ res = nvme_trans_send_activate_fw_cmd(ns, hdr, buffer_id);
break;
default:
res = nvme_trans_completion(hdr, SAM_STAT_CHECK_CONDITION,
@@ -2890,15 +2374,13 @@ struct scsi_unmap_parm_list {
static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *cmd)
{
- struct nvme_dev *dev = ns->dev;
struct scsi_unmap_parm_list *plist;
struct nvme_dsm_range *range;
struct nvme_command c;
- int i, nvme_sc, res = -ENOMEM;
+ int i, nvme_sc, res;
u16 ndesc, list_len;
- dma_addr_t dma_addr;
- list_len = GET_U16_FROM_CDB(cmd, UNMAP_CDB_PARAM_LIST_LENGTH_OFFSET);
+ list_len = get_unaligned_be16(&cmd[7]);
if (!list_len)
return -EINVAL;
@@ -2907,7 +2389,7 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
return -ENOMEM;
res = nvme_trans_copy_from_user(hdr, plist, list_len);
- if (res != SNTI_TRANSLATION_SUCCESS)
+ if (res)
goto out;
ndesc = be16_to_cpu(plist->unmap_blk_desc_data_len) >> 4;
@@ -2916,10 +2398,11 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
goto out;
}
- range = dma_alloc_coherent(&dev->pci_dev->dev, ndesc * sizeof(*range),
- &dma_addr, GFP_KERNEL);
- if (!range)
+ range = kcalloc(ndesc, sizeof(*range), GFP_KERNEL);
+ if (!range) {
+ res = -ENOMEM;
goto out;
+ }
for (i = 0; i < ndesc; i++) {
range[i].nlb = cpu_to_le32(be32_to_cpu(plist->desc[i].nlb));
@@ -2930,15 +2413,14 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
memset(&c, 0, sizeof(c));
c.dsm.opcode = nvme_cmd_dsm;
c.dsm.nsid = cpu_to_le32(ns->ns_id);
- c.dsm.prp1 = cpu_to_le64(dma_addr);
c.dsm.nr = cpu_to_le32(ndesc - 1);
c.dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
- nvme_sc = nvme_submit_io_cmd(dev, ns, &c, NULL);
+ nvme_sc = nvme_submit_sync_cmd(ns->queue, &c, range,
+ ndesc * sizeof(*range));
res = nvme_trans_status_code(hdr, nvme_sc);
- dma_free_coherent(&dev->pci_dev->dev, ndesc * sizeof(*range),
- range, dma_addr);
+ kfree(range);
out:
kfree(plist);
return res;
@@ -2993,13 +2475,16 @@ static int nvme_scsi_translate(struct nvme_ns *ns, struct sg_io_hdr *hdr)
retcode = nvme_trans_mode_sense(ns, hdr, cmd);
break;
case READ_CAPACITY:
- retcode = nvme_trans_read_capacity(ns, hdr, cmd);
+ retcode = nvme_trans_read_capacity(ns, hdr, cmd, 0);
break;
case SERVICE_ACTION_IN_16:
- if (IS_READ_CAP_16(cmd))
- retcode = nvme_trans_read_capacity(ns, hdr, cmd);
- else
+ switch (cmd[1]) {
+ case SAI_READ_CAPACITY_16:
+ retcode = nvme_trans_read_capacity(ns, hdr, cmd, 1);
+ break;
+ default:
goto out;
+ }
break;
case REPORT_LUNS:
retcode = nvme_trans_report_luns(ns, hdr, cmd);
@@ -3015,7 +2500,7 @@ static int nvme_scsi_translate(struct nvme_ns *ns, struct sg_io_hdr *hdr)
retcode = nvme_trans_start_stop(ns, hdr, cmd);
break;
case SYNCHRONIZE_CACHE:
- retcode = nvme_trans_synchronize_cache(ns, hdr, cmd);
+ retcode = nvme_trans_synchronize_cache(ns, hdr);
break;
case FORMAT_UNIT:
retcode = nvme_trans_format_unit(ns, hdr, cmd);
@@ -3053,15 +2538,16 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr)
if (hdr.cmd_len > BLK_MAX_CDB)
return -EINVAL;
+ /*
+ * A positive return code means a NVMe status, which has been
+ * translated to sense data.
+ */
retcode = nvme_scsi_translate(ns, &hdr);
if (retcode < 0)
return retcode;
- if (retcode > 0)
- retcode = SNTI_TRANSLATION_SUCCESS;
if (copy_to_user(u_hdr, &hdr, sizeof(sg_io_hdr_t)) > 0)
return -EFAULT;
-
- return retcode;
+ return 0;
}
int nvme_sg_get_version_num(int __user *ip)
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index d48715b..dbb4da1 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -442,7 +442,7 @@ static char *pd_buf; /* buffer for request in progress */
static enum action do_pd_io_start(void)
{
- if (pd_req->cmd_type == REQ_TYPE_SPECIAL) {
+ if (pd_req->cmd_type == REQ_TYPE_DRV_PRIV) {
phase = pd_special;
return pd_special();
}
@@ -725,7 +725,7 @@ static int pd_special_command(struct pd_unit *disk,
if (IS_ERR(rq))
return PTR_ERR(rq);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->special = func;
err = blk_execute_rq(disk->gd->queue, disk->gd, rq, 0);
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 09e628da..4c20c22 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -61,6 +61,7 @@
#include <linux/freezer.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/backing-dev.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi.h>
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index ef45cfb..b1612eb 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -1,5 +1,5 @@
/*
- * ps3vram - Use extra PS3 video ram as MTD block device.
+ * ps3vram - Use extra PS3 video ram as block device.
*
* Copyright 2009 Sony Corporation
*
@@ -73,8 +73,8 @@ struct ps3vram_priv {
u64 memory_handle;
u64 context_handle;
- u32 *ctrl;
- void *reports;
+ u32 __iomem *ctrl;
+ void __iomem *reports;
u8 *xdr_buf;
u32 *fifo_base;
@@ -104,7 +104,7 @@ static char *size = "256M";
module_param(size, charp, 0);
MODULE_PARM_DESC(size, "memory size");
-static u32 *ps3vram_get_notifier(void *reports, int notifier)
+static u32 __iomem *ps3vram_get_notifier(void __iomem *reports, int notifier)
{
return reports + DMA_NOTIFIER_OFFSET_BASE +
DMA_NOTIFIER_SIZE * notifier;
@@ -113,22 +113,22 @@ static u32 *ps3vram_get_notifier(void *reports, int notifier)
static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
{
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
- u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
+ u32 __iomem *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
int i;
for (i = 0; i < 4; i++)
- notify[i] = 0xffffffff;
+ iowrite32be(0xffffffff, notify + i);
}
static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
unsigned int timeout_ms)
{
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
- u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
+ u32 __iomem *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
unsigned long timeout;
for (timeout = 20; timeout; timeout--) {
- if (!notify[3])
+ if (!ioread32be(notify + 3))
return 0;
udelay(10);
}
@@ -136,7 +136,7 @@ static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
- if (!notify[3])
+ if (!ioread32be(notify + 3))
return 0;
msleep(1);
} while (time_before(jiffies, timeout));
@@ -148,8 +148,8 @@ static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
{
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
- priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
- priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
+ iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_PUT);
+ iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_GET);
}
static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
@@ -159,14 +159,14 @@ static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
- if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
+ if (ioread32be(priv->ctrl + CTRL_PUT) == ioread32be(priv->ctrl + CTRL_GET))
return 0;
msleep(1);
} while (time_before(jiffies, timeout));
dev_warn(&dev->core, "FIFO timeout (%08x/%08x/%08x)\n",
- priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET],
- priv->ctrl[CTRL_TOP]);
+ ioread32be(priv->ctrl + CTRL_PUT), ioread32be(priv->ctrl + CTRL_GET),
+ ioread32be(priv->ctrl + CTRL_TOP));
return -ETIMEDOUT;
}
@@ -189,7 +189,7 @@ static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
- priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
+ iowrite32be(FIFO_BASE + FIFO_OFFSET, priv->ctrl + CTRL_PUT);
/* asking the HV for a blit will kick the FIFO */
status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
@@ -207,8 +207,8 @@ static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
mutex_lock(&ps3_gpu_mutex);
- priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
- (priv->fifo_ptr - priv->fifo_base) * sizeof(u32);
+ iowrite32be(FIFO_BASE + FIFO_OFFSET + (priv->fifo_ptr - priv->fifo_base)
+ * sizeof(u32), priv->ctrl + CTRL_PUT);
/* asking the HV for a blit will kick the FIFO */
status = lv1_gpu_fb_blit(priv->context_handle, 0, 0, 0, 0);
diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
index 5d55285..59c91d4 100644
--- a/drivers/block/sx8.c
+++ b/drivers/block/sx8.c
@@ -620,7 +620,7 @@ static int carm_array_info (struct carm_host *host, unsigned int array_idx)
spin_unlock_irq(&host->lock);
DPRINTK("blk_execute_rq_nowait, tag == %u\n", idx);
- crq->rq->cmd_type = REQ_TYPE_SPECIAL;
+ crq->rq->cmd_type = REQ_TYPE_DRV_PRIV;
crq->rq->special = crq;
blk_execute_rq_nowait(host->oob_q, NULL, crq->rq, true, NULL);
@@ -661,7 +661,7 @@ static int carm_send_special (struct carm_host *host, carm_sspc_t func)
crq->msg_bucket = (u32) rc;
DPRINTK("blk_execute_rq_nowait, tag == %u\n", idx);
- crq->rq->cmd_type = REQ_TYPE_SPECIAL;
+ crq->rq->cmd_type = REQ_TYPE_DRV_PRIV;
crq->rq->special = crq;
blk_execute_rq_nowait(host->oob_q, NULL, crq->rq, true, NULL);
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 5ea2f0b..d4d05f0 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -124,7 +124,7 @@ static inline void virtblk_request_done(struct request *req)
req->resid_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.residual);
req->sense_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.sense_len);
req->errors = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.errors);
- } else if (req->cmd_type == REQ_TYPE_SPECIAL) {
+ } else if (req->cmd_type == REQ_TYPE_DRV_PRIV) {
req->errors = (error != 0);
}
@@ -188,7 +188,7 @@ static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
break;
- case REQ_TYPE_SPECIAL:
+ case REQ_TYPE_DRV_PRIV:
vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
@@ -251,7 +251,7 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
return PTR_ERR(req);
}
- req->cmd_type = REQ_TYPE_SPECIAL;
+ req->cmd_type = REQ_TYPE_DRV_PRIV;
err = blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
blk_put_request(req);
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 5fc291c6..60316fb 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -12,6 +12,7 @@
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/module.h>
#include <linux/raw.h>
#include <linux/capability.h>
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3d00c25..9df871d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
+obj-$(CONFIG_H8300) += h8300/
diff --git a/drivers/clk/h8300/Makefile b/drivers/clk/h8300/Makefile
new file mode 100644
index 0000000..b86427c3
--- /dev/null
+++ b/drivers/clk/h8300/Makefile
@@ -0,0 +1,2 @@
+obj-y += clk-div.o
+obj-$(CONFIG_H8S2678) += clk-h8s2678.o
diff --git a/drivers/clk/h8300/clk-div.c b/drivers/clk/h8300/clk-div.c
new file mode 100644
index 0000000..56f9eba
--- /dev/null
+++ b/drivers/clk/h8300/clk-div.c
@@ -0,0 +1,53 @@
+/*
+ * H8/300 divide clock driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+static DEFINE_SPINLOCK(clklock);
+
+static void __init h8300_div_clk_setup(struct device_node *node)
+{
+ unsigned int num_parents;
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ void __iomem *divcr = NULL;
+ int width;
+
+ num_parents = of_clk_get_parent_count(node);
+ if (num_parents < 1) {
+ pr_err("%s: no parent found", clk_name);
+ return;
+ }
+
+ divcr = of_iomap(node, 0);
+ if (divcr == NULL) {
+ pr_err("%s: failed to map divide register", clk_name);
+ goto error;
+ }
+
+ parent_name = of_clk_get_parent_name(node, 0);
+ of_property_read_u32(node, "renesas,width", &width);
+ clk = clk_register_divider(NULL, clk_name, parent_name,
+ CLK_SET_RATE_GATE, divcr, 0, width,
+ CLK_DIVIDER_POWER_OF_TWO, &clklock);
+ if (!IS_ERR(clk)) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return;
+ }
+ pr_err("%s: failed to register %s div clock (%ld)\n",
+ __func__, clk_name, PTR_ERR(clk));
+error:
+ if (divcr)
+ iounmap(divcr);
+}
+
+CLK_OF_DECLARE(h8300_div_clk, "renesas,h8300-div-clock", h8300_div_clk_setup);
diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c
new file mode 100644
index 0000000..4701b093
--- /dev/null
+++ b/drivers/clk/h8300/clk-h8s2678.c
@@ -0,0 +1,146 @@
+/*
+ * H8S2678 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+
+static DEFINE_SPINLOCK(clklock);
+
+#define MAX_FREQ 33333333
+#define MIN_FREQ 8000000
+
+struct pll_clock {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ void __iomem *pllcr;
+};
+
+#define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw)
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pll_clock *pll_clock = to_pll_clock(hw);
+ int mul = 1 << (ctrl_inb((unsigned long)pll_clock->pllcr) & 3);
+
+ return parent_rate * mul;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int i, m = -1;
+ long offset[3];
+
+ if (rate > MAX_FREQ)
+ rate = MAX_FREQ;
+ if (rate < MIN_FREQ)
+ rate = MIN_FREQ;
+
+ for (i = 0; i < 3; i++)
+ offset[i] = abs(rate - (*prate * (1 << i)));
+ for (i = 0; i < 3; i++)
+ if (m < 0)
+ m = i;
+ else
+ m = (offset[i] < offset[m])?i:m;
+
+ return *prate * (1 << m);
+}
+
+static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int pll;
+ unsigned char val;
+ unsigned long flags;
+ struct pll_clock *pll_clock = to_pll_clock(hw);
+
+ pll = ((rate / parent_rate) / 2) & 0x03;
+ spin_lock_irqsave(&clklock, flags);
+ val = ctrl_inb((unsigned long)pll_clock->sckcr);
+ val |= 0x08;
+ ctrl_outb(val, (unsigned long)pll_clock->sckcr);
+ val = ctrl_inb((unsigned long)pll_clock->pllcr);
+ val &= ~0x03;
+ val |= pll;
+ ctrl_outb(val, (unsigned long)pll_clock->pllcr);
+ spin_unlock_irqrestore(&clklock, flags);
+ return 0;
+}
+
+static const struct clk_ops pll_ops = {
+ .recalc_rate = pll_recalc_rate,
+ .round_rate = pll_round_rate,
+ .set_rate = pll_set_rate,
+};
+
+static void __init h8s2678_pll_clk_setup(struct device_node *node)
+{
+ unsigned int num_parents;
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct pll_clock *pll_clock;
+ struct clk_init_data init;
+
+ num_parents = of_clk_get_parent_count(node);
+ if (num_parents < 1) {
+ pr_err("%s: no parent found", clk_name);
+ return;
+ }
+
+
+ pll_clock = kzalloc(sizeof(struct pll_clock), GFP_KERNEL);
+ if (!pll_clock) {
+ pr_err("%s: failed to alloc memory", clk_name);
+ return;
+ }
+
+ pll_clock->sckcr = of_iomap(node, 0);
+ if (pll_clock->sckcr == NULL) {
+ pr_err("%s: failed to map divide register", clk_name);
+ goto free_clock;
+ }
+
+ pll_clock->pllcr = of_iomap(node, 1);
+ if (pll_clock->pllcr == NULL) {
+ pr_err("%s: failed to map multiply register", clk_name);
+ goto unmap_sckcr;
+ }
+
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.name = clk_name;
+ init.ops = &pll_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ pll_clock->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_clock->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register %s div clock (%ld)\n",
+ __func__, clk_name, PTR_ERR(clk));
+ goto unmap_pllcr;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return;
+
+unmap_pllcr:
+ iounmap(pll_clock->pllcr);
+unmap_sckcr:
+ iounmap(pll_clock->sckcr);
+free_clock:
+ kfree(pll_clock);
+}
+
+CLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock",
+ h8s2678_pll_clk_setup);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 32164ba..d1bd53f 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -275,4 +275,11 @@ config CLKSRC_PXA
help
This enables OST0 support available on PXA and SA-11x0
platforms.
+
+config H8300_TMR16
+ bool
+
+config H8300_TPU
+ bool
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 1831a58..2b34423 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -55,3 +55,6 @@ obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o
obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
+obj-$(CONFIG_H8300) += h8300_timer8.o
+obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
+obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
new file mode 100644
index 0000000..82941c1
--- /dev/null
+++ b/drivers/clocksource/h8300_timer16.c
@@ -0,0 +1,254 @@
+/*
+ * H8/300 16bit Timer driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clocksource.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/segment.h>
+#include <asm/irq.h>
+
+#define TSTR 0
+#define TSNC 1
+#define TMDR 2
+#define TOLR 3
+#define TISRA 4
+#define TISRB 5
+#define TISRC 6
+
+#define TCR 0
+#define TIOR 1
+#define TCNT 2
+#define GRA 4
+#define GRB 6
+
+#define FLAG_REPROGRAM (1 << 0)
+#define FLAG_SKIPEVENT (1 << 1)
+#define FLAG_IRQCONTEXT (1 << 2)
+#define FLAG_STARTED (1 << 3)
+
+#define ONESHOT 0
+#define PERIODIC 1
+
+#define RELATIVE 0
+#define ABSOLUTE 1
+
+struct timer16_priv {
+ struct platform_device *pdev;
+ struct clocksource cs;
+ struct irqaction irqaction;
+ unsigned long total_cycles;
+ unsigned long mapbase;
+ unsigned long mapcommon;
+ unsigned long flags;
+ unsigned short gra;
+ unsigned short cs_enabled;
+ unsigned char enb;
+ unsigned char imfa;
+ unsigned char imiea;
+ unsigned char ovf;
+ raw_spinlock_t lock;
+ struct clk *clk;
+};
+
+static unsigned long timer16_get_counter(struct timer16_priv *p)
+{
+ unsigned long v1, v2, v3;
+ int o1, o2;
+
+ o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
+
+ /* Make sure the timer value is stable. Stolen from acpi_pm.c */
+ do {
+ o2 = o1;
+ v1 = ctrl_inw(p->mapbase + TCNT);
+ v2 = ctrl_inw(p->mapbase + TCNT);
+ v3 = ctrl_inw(p->mapbase + TCNT);
+ o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
+ } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
+ || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
+
+ v2 |= 0x10000;
+ return v2;
+}
+
+
+static irqreturn_t timer16_interrupt(int irq, void *dev_id)
+{
+ struct timer16_priv *p = (struct timer16_priv *)dev_id;
+
+ ctrl_outb(ctrl_inb(p->mapcommon + TISRA) & ~p->imfa,
+ p->mapcommon + TISRA);
+ p->total_cycles += 0x10000;
+
+ return IRQ_HANDLED;
+}
+
+static inline struct timer16_priv *cs_to_priv(struct clocksource *cs)
+{
+ return container_of(cs, struct timer16_priv, cs);
+}
+
+static cycle_t timer16_clocksource_read(struct clocksource *cs)
+{
+ struct timer16_priv *p = cs_to_priv(cs);
+ unsigned long flags, raw;
+ unsigned long value;
+
+ raw_spin_lock_irqsave(&p->lock, flags);
+ value = p->total_cycles;
+ raw = timer16_get_counter(p);
+ raw_spin_unlock_irqrestore(&p->lock, flags);
+
+ return value + raw;
+}
+
+static int timer16_enable(struct clocksource *cs)
+{
+ struct timer16_priv *p = cs_to_priv(cs);
+
+ WARN_ON(p->cs_enabled);
+
+ p->total_cycles = 0;
+ ctrl_outw(0x0000, p->mapbase + TCNT);
+ ctrl_outb(0x83, p->mapbase + TCR);
+ ctrl_outb(ctrl_inb(p->mapcommon + TSTR) | p->enb,
+ p->mapcommon + TSTR);
+
+ p->cs_enabled = true;
+ return 0;
+}
+
+static void timer16_disable(struct clocksource *cs)
+{
+ struct timer16_priv *p = cs_to_priv(cs);
+
+ WARN_ON(!p->cs_enabled);
+
+ ctrl_outb(ctrl_inb(p->mapcommon + TSTR) & ~p->enb,
+ p->mapcommon + TSTR);
+
+ p->cs_enabled = false;
+}
+
+#define REG_CH 0
+#define REG_COMM 1
+
+static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev)
+{
+ struct resource *res[2];
+ int ret, irq;
+ unsigned int ch;
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ res[REG_CH] = platform_get_resource(p->pdev,
+ IORESOURCE_MEM, REG_CH);
+ res[REG_COMM] = platform_get_resource(p->pdev,
+ IORESOURCE_MEM, REG_COMM);
+ if (!res[REG_CH] || !res[REG_COMM]) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
+ irq = platform_get_irq(p->pdev, 0);
+ if (irq < 0) {
+ dev_err(&p->pdev->dev, "failed to get irq\n");
+ return irq;
+ }
+
+ p->clk = clk_get(&p->pdev->dev, "fck");
+ if (IS_ERR(p->clk)) {
+ dev_err(&p->pdev->dev, "can't get clk\n");
+ return PTR_ERR(p->clk);
+ }
+ of_property_read_u32(p->pdev->dev.of_node, "renesas,channel", &ch);
+
+ p->pdev = pdev;
+ p->mapbase = res[REG_CH]->start;
+ p->mapcommon = res[REG_COMM]->start;
+ p->enb = 1 << ch;
+ p->imfa = 1 << ch;
+ p->imiea = 1 << (4 + ch);
+ p->cs.name = pdev->name;
+ p->cs.rating = 200;
+ p->cs.read = timer16_clocksource_read;
+ p->cs.enable = timer16_enable;
+ p->cs.disable = timer16_disable;
+ p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
+ p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+ ret = request_irq(irq, timer16_interrupt,
+ IRQF_TIMER, pdev->name, p);
+ if (ret < 0) {
+ dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
+ return ret;
+ }
+
+ clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 8);
+
+ return 0;
+}
+
+static int timer16_probe(struct platform_device *pdev)
+{
+ struct timer16_priv *p = platform_get_drvdata(pdev);
+
+ if (p) {
+ dev_info(&pdev->dev, "kept as earlytimer\n");
+ return 0;
+ }
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ return timer16_setup(p, pdev);
+}
+
+static int timer16_remove(struct platform_device *pdev)
+{
+ return -EBUSY;
+}
+
+static const struct of_device_id timer16_of_table[] = {
+ { .compatible = "renesas,16bit-timer" },
+ { }
+};
+static struct platform_driver timer16_driver = {
+ .probe = timer16_probe,
+ .remove = timer16_remove,
+ .driver = {
+ .name = "h8300h-16timer",
+ .of_match_table = of_match_ptr(timer16_of_table),
+ }
+};
+
+static int __init timer16_init(void)
+{
+ return platform_driver_register(&timer16_driver);
+}
+
+static void __exit timer16_exit(void)
+{
+ platform_driver_unregister(&timer16_driver);
+}
+
+subsys_initcall(timer16_init);
+module_exit(timer16_exit);
+MODULE_AUTHOR("Yoshinori Sato");
+MODULE_DESCRIPTION("H8/300H 16bit Timer Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
new file mode 100644
index 0000000..0214cb3
--- /dev/null
+++ b/drivers/clocksource/h8300_timer8.c
@@ -0,0 +1,313 @@
+/*
+ * linux/arch/h8300/kernel/cpu/timer/timer8.c
+ *
+ * Yoshinori Sato <ysato@users.sourcefoge.jp>
+ *
+ * 8bit Timer driver
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clockchips.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/irq.h>
+
+#define _8TCR 0
+#define _8TCSR 2
+#define TCORA 4
+#define TCORB 6
+#define _8TCNT 8
+
+#define FLAG_REPROGRAM (1 << 0)
+#define FLAG_SKIPEVENT (1 << 1)
+#define FLAG_IRQCONTEXT (1 << 2)
+#define FLAG_STARTED (1 << 3)
+
+#define ONESHOT 0
+#define PERIODIC 1
+
+#define RELATIVE 0
+#define ABSOLUTE 1
+
+struct timer8_priv {
+ struct platform_device *pdev;
+ struct clock_event_device ced;
+ struct irqaction irqaction;
+ unsigned long mapbase;
+ raw_spinlock_t lock;
+ unsigned long flags;
+ unsigned int rate;
+ unsigned int tcora;
+ struct clk *pclk;
+};
+
+static unsigned long timer8_get_counter(struct timer8_priv *p)
+{
+ unsigned long v1, v2, v3;
+ int o1, o2;
+
+ o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
+
+ /* Make sure the timer value is stable. Stolen from acpi_pm.c */
+ do {
+ o2 = o1;
+ v1 = ctrl_inw(p->mapbase + _8TCNT);
+ v2 = ctrl_inw(p->mapbase + _8TCNT);
+ v3 = ctrl_inw(p->mapbase + _8TCNT);
+ o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
+ } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
+ || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
+
+ v2 |= o1 << 10;
+ return v2;
+}
+
+static irqreturn_t timer8_interrupt(int irq, void *dev_id)
+{
+ struct timer8_priv *p = dev_id;
+
+ ctrl_outb(ctrl_inb(p->mapbase + _8TCSR) & ~0x40,
+ p->mapbase + _8TCSR);
+ p->flags |= FLAG_IRQCONTEXT;
+ ctrl_outw(p->tcora, p->mapbase + TCORA);
+ if (!(p->flags & FLAG_SKIPEVENT)) {
+ if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT)
+ ctrl_outw(0x0000, p->mapbase + _8TCR);
+ p->ced.event_handler(&p->ced);
+ }
+ p->flags &= ~(FLAG_SKIPEVENT | FLAG_IRQCONTEXT);
+
+ return IRQ_HANDLED;
+}
+
+static void timer8_set_next(struct timer8_priv *p, unsigned long delta)
+{
+ unsigned long flags;
+ unsigned long now;
+
+ raw_spin_lock_irqsave(&p->lock, flags);
+ if (delta >= 0x10000)
+ dev_warn(&p->pdev->dev, "delta out of range\n");
+ now = timer8_get_counter(p);
+ p->tcora = delta;
+ ctrl_outb(ctrl_inb(p->mapbase + _8TCR) | 0x40, p->mapbase + _8TCR);
+ if (delta > now)
+ ctrl_outw(delta, p->mapbase + TCORA);
+ else
+ ctrl_outw(now + 1, p->mapbase + TCORA);
+
+ raw_spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int timer8_enable(struct timer8_priv *p)
+{
+ p->rate = clk_get_rate(p->pclk) / 64;
+ ctrl_outw(0xffff, p->mapbase + TCORA);
+ ctrl_outw(0x0000, p->mapbase + _8TCNT);
+ ctrl_outw(0x0c02, p->mapbase + _8TCR);
+
+ return 0;
+}
+
+static int timer8_start(struct timer8_priv *p)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&p->lock, flags);
+
+ if (!(p->flags & FLAG_STARTED))
+ ret = timer8_enable(p);
+
+ if (ret)
+ goto out;
+ p->flags |= FLAG_STARTED;
+
+ out:
+ raw_spin_unlock_irqrestore(&p->lock, flags);
+
+ return ret;
+}
+
+static void timer8_stop(struct timer8_priv *p)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&p->lock, flags);
+
+ ctrl_outw(0x0000, p->mapbase + _8TCR);
+
+ raw_spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
+{
+ return container_of(ced, struct timer8_priv, ced);
+}
+
+static void timer8_clock_event_start(struct timer8_priv *p, int periodic)
+{
+ struct clock_event_device *ced = &p->ced;
+
+ timer8_start(p);
+
+ ced->shift = 32;
+ ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
+ ced->max_delta_ns = clockevent_delta2ns(0xffff, ced);
+ ced->min_delta_ns = clockevent_delta2ns(0x0001, ced);
+
+ timer8_set_next(p, periodic?(p->rate + HZ/2) / HZ:0x10000);
+}
+
+static void timer8_clock_event_mode(enum clock_event_mode mode,
+ struct clock_event_device *ced)
+{
+ struct timer8_priv *p = ced_to_priv(ced);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ dev_info(&p->pdev->dev, "used for periodic clock events\n");
+ timer8_stop(p);
+ timer8_clock_event_start(p, PERIODIC);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ dev_info(&p->pdev->dev, "used for oneshot clock events\n");
+ timer8_stop(p);
+ timer8_clock_event_start(p, ONESHOT);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ timer8_stop(p);
+ break;
+ default:
+ break;
+ }
+}
+
+static int timer8_clock_event_next(unsigned long delta,
+ struct clock_event_device *ced)
+{
+ struct timer8_priv *p = ced_to_priv(ced);
+
+ BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
+ timer8_set_next(p, delta - 1);
+
+ return 0;
+}
+
+static int timer8_setup(struct timer8_priv *p,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+ int ret;
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(p->pdev, 0);
+ if (irq < 0) {
+ dev_err(&p->pdev->dev, "failed to get irq\n");
+ return -ENXIO;
+ }
+
+ p->mapbase = res->start;
+
+ p->irqaction.name = dev_name(&p->pdev->dev);
+ p->irqaction.handler = timer8_interrupt;
+ p->irqaction.dev_id = p;
+ p->irqaction.flags = IRQF_TIMER;
+
+ p->pclk = clk_get(&p->pdev->dev, "fck");
+ if (IS_ERR(p->pclk)) {
+ dev_err(&p->pdev->dev, "can't get clk\n");
+ return PTR_ERR(p->pclk);
+ }
+
+ p->ced.name = pdev->name;
+ p->ced.features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT;
+ p->ced.rating = 200;
+ p->ced.cpumask = cpumask_of(0);
+ p->ced.set_next_event = timer8_clock_event_next;
+ p->ced.set_mode = timer8_clock_event_mode;
+
+ ret = setup_irq(irq, &p->irqaction);
+ if (ret < 0) {
+ dev_err(&p->pdev->dev,
+ "failed to request irq %d\n", irq);
+ return ret;
+ }
+ clockevents_register_device(&p->ced);
+ platform_set_drvdata(pdev, p);
+
+ return 0;
+}
+
+static int timer8_probe(struct platform_device *pdev)
+{
+ struct timer8_priv *p = platform_get_drvdata(pdev);
+
+ if (p) {
+ dev_info(&pdev->dev, "kept as earlytimer\n");
+ return 0;
+ }
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ return timer8_setup(p, pdev);
+}
+
+static int timer8_remove(struct platform_device *pdev)
+{
+ return -EBUSY;
+}
+
+static const struct of_device_id timer8_of_table[] __maybe_unused = {
+ { .compatible = "renesas,8bit-timer" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, timer8_of_table);
+static struct platform_driver timer8_driver = {
+ .probe = timer8_probe,
+ .remove = timer8_remove,
+ .driver = {
+ .name = "h8300-8timer",
+ .of_match_table = of_match_ptr(timer8_of_table),
+ }
+};
+
+static int __init timer8_init(void)
+{
+ return platform_driver_register(&timer8_driver);
+}
+
+static void __exit timer8_exit(void)
+{
+ platform_driver_unregister(&timer8_driver);
+}
+
+subsys_initcall(timer8_init);
+module_exit(timer8_exit);
+MODULE_AUTHOR("Yoshinori Sato");
+MODULE_DESCRIPTION("H8/300 8bit Timer Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
new file mode 100644
index 0000000..64195fd
--- /dev/null
+++ b/drivers/clocksource/h8300_tpu.c
@@ -0,0 +1,207 @@
+/*
+ * H8/300 TPU Driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clocksource.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include <asm/irq.h>
+
+#define TCR 0
+#define TMDR 1
+#define TIOR 2
+#define TER 4
+#define TSR 5
+#define TCNT 6
+#define TGRA 8
+#define TGRB 10
+#define TGRC 12
+#define TGRD 14
+
+struct tpu_priv {
+ struct platform_device *pdev;
+ struct clocksource cs;
+ struct clk *clk;
+ unsigned long mapbase1;
+ unsigned long mapbase2;
+ raw_spinlock_t lock;
+ unsigned int cs_enabled;
+};
+
+static inline unsigned long read_tcnt32(struct tpu_priv *p)
+{
+ unsigned long tcnt;
+
+ tcnt = ctrl_inw(p->mapbase1 + TCNT) << 16;
+ tcnt |= ctrl_inw(p->mapbase2 + TCNT);
+ return tcnt;
+}
+
+static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
+{
+ unsigned long v1, v2, v3;
+ int o1, o2;
+
+ o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
+
+ /* Make sure the timer value is stable. Stolen from acpi_pm.c */
+ do {
+ o2 = o1;
+ v1 = read_tcnt32(p);
+ v2 = read_tcnt32(p);
+ v3 = read_tcnt32(p);
+ o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
+ } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
+ || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
+
+ *val = v2;
+ return o1;
+}
+
+static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
+{
+ return container_of(cs, struct tpu_priv, cs);
+}
+
+static cycle_t tpu_clocksource_read(struct clocksource *cs)
+{
+ struct tpu_priv *p = cs_to_priv(cs);
+ unsigned long flags;
+ unsigned long long value;
+
+ raw_spin_lock_irqsave(&p->lock, flags);
+ if (tpu_get_counter(p, &value))
+ value += 0x100000000;
+ raw_spin_unlock_irqrestore(&p->lock, flags);
+
+ return value;
+}
+
+static int tpu_clocksource_enable(struct clocksource *cs)
+{
+ struct tpu_priv *p = cs_to_priv(cs);
+
+ WARN_ON(p->cs_enabled);
+
+ ctrl_outw(0, p->mapbase1 + TCNT);
+ ctrl_outw(0, p->mapbase2 + TCNT);
+ ctrl_outb(0x0f, p->mapbase1 + TCR);
+ ctrl_outb(0x03, p->mapbase2 + TCR);
+
+ p->cs_enabled = true;
+ return 0;
+}
+
+static void tpu_clocksource_disable(struct clocksource *cs)
+{
+ struct tpu_priv *p = cs_to_priv(cs);
+
+ WARN_ON(!p->cs_enabled);
+
+ ctrl_outb(0, p->mapbase1 + TCR);
+ ctrl_outb(0, p->mapbase2 + TCR);
+ p->cs_enabled = false;
+}
+
+#define CH_L 0
+#define CH_H 1
+
+static int __init tpu_setup(struct tpu_priv *p, struct platform_device *pdev)
+{
+ struct resource *res[2];
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ res[CH_L] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_L);
+ res[CH_H] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_H);
+ if (!res[CH_L] || !res[CH_H]) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
+
+ p->clk = clk_get(&p->pdev->dev, "fck");
+ if (IS_ERR(p->clk)) {
+ dev_err(&p->pdev->dev, "can't get clk\n");
+ return PTR_ERR(p->clk);
+ }
+
+ p->mapbase1 = res[CH_L]->start;
+ p->mapbase2 = res[CH_H]->start;
+
+ p->cs.name = pdev->name;
+ p->cs.rating = 200;
+ p->cs.read = tpu_clocksource_read;
+ p->cs.enable = tpu_clocksource_enable;
+ p->cs.disable = tpu_clocksource_disable;
+ p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
+ p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 64);
+ platform_set_drvdata(pdev, p);
+
+ return 0;
+}
+
+static int tpu_probe(struct platform_device *pdev)
+{
+ struct tpu_priv *p = platform_get_drvdata(pdev);
+
+ if (p) {
+ dev_info(&pdev->dev, "kept as earlytimer\n");
+ return 0;
+ }
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ return tpu_setup(p, pdev);
+}
+
+static int tpu_remove(struct platform_device *pdev)
+{
+ return -EBUSY;
+}
+
+static const struct of_device_id tpu_of_table[] = {
+ { .compatible = "renesas,tpu" },
+ { }
+};
+
+static struct platform_driver tpu_driver = {
+ .probe = tpu_probe,
+ .remove = tpu_remove,
+ .driver = {
+ .name = "h8s-tpu",
+ .of_match_table = of_match_ptr(tpu_of_table),
+ }
+};
+
+static int __init tpu_init(void)
+{
+ return platform_driver_register(&tpu_driver);
+}
+
+static void __exit tpu_exit(void)
+{
+ platform_driver_unregister(&tpu_driver);
+}
+
+subsys_initcall(tpu_init);
+module_exit(tpu_exit);
+MODULE_AUTHOR("Yoshinori Sato");
+MODULE_DESCRIPTION("H8S Timer Pulse Unit Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index a18d16c..e0302c7 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -465,6 +465,7 @@ static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
+ unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@@ -482,10 +483,10 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&desc->node, &list);
}
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.free);
list_add_tail(&page->node, &chan->desc.pages);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@@ -516,6 +517,7 @@ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan,
static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc, *_desc;
+ unsigned long flags;
LIST_HEAD(list);
/*
@@ -524,9 +526,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
* list_for_each_entry_safe, isn't safe if we release the channel lock
* around the rcar_dmac_desc_put() call.
*/
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
list_splice_init(&chan->desc.wait, &list);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, node) {
if (async_tx_test_ack(&desc->async_tx)) {
@@ -539,9 +541,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
return;
/* Put the remaining descriptors back in the wait list. */
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
list_splice(&list, &chan->desc.wait);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
}
/*
@@ -556,12 +558,13 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc;
+ unsigned long flags;
int ret;
/* Recycle acked descriptors before attempting allocation. */
rcar_dmac_desc_recycle_acked(chan);
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.free)) {
/*
@@ -570,17 +573,17 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
}
desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
list_del(&desc->node);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
return desc;
}
@@ -593,6 +596,7 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
+ unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@@ -606,10 +610,10 @@ static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&chunk->node, &list);
}
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.chunks_free);
list_add_tail(&page->node, &chan->desc.pages);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_xfer_chunk *chunk;
+ unsigned long flags;
int ret;
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.chunks_free)) {
/*
@@ -638,18 +643,18 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
- spin_lock_irq(&chan->lock);
+ spin_lock_irqsave(&chan->lock, flags);
}
chunk = list_first_entry(&chan->desc.chunks_free,
struct rcar_dmac_xfer_chunk, node);
list_del(&chunk->node);
- spin_unlock_irq(&chan->lock);
+ spin_unlock_irqrestore(&chan->lock, flags);
return chunk;
}
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 112d63a..33df7d9 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -307,8 +307,6 @@ static struct device_type csrow_attr_type = {
*
*/
-#define EDAC_NR_CHANNELS 6
-
DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 0);
DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
@@ -403,9 +401,6 @@ static inline int nr_pages_per_csrow(struct csrow_info *csrow)
static int edac_create_csrow_object(struct mem_ctl_info *mci,
struct csrow_info *csrow, int index)
{
- if (csrow->nr_channels > EDAC_NR_CHANNELS)
- return -ENODEV;
-
csrow->dev.type = &csrow_attr_type;
csrow->dev.bus = mci->bus;
csrow->dev.groups = csrow_dev_groups;
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 1acf57b..ca78311 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -34,7 +34,7 @@ static int probed;
/*
* Alter this version for the module when modifications are made
*/
-#define SBRIDGE_REVISION " Ver: 1.1.0 "
+#define SBRIDGE_REVISION " Ver: 1.1.1 "
#define EDAC_MOD_STR "sbridge_edac"
/*
@@ -254,7 +254,7 @@ static const u32 correrrthrsld[] = {
* sbridge structs
*/
-#define NUM_CHANNELS 4
+#define NUM_CHANNELS 8 /* 2MC per socket, four chan per MC */
#define MAX_DIMMS 3 /* Max DIMMS per channel */
#define CHANNEL_UNSPECIFIED 0xf /* Intel IA32 SDM 15-14 */
@@ -393,6 +393,8 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_RAS 0x0e79
#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0 0x0e6a
#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1 0x0e6b
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD2 0x0e6c
+#define PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD3 0x0e6d
static const struct pci_id_descr pci_dev_descr_ibridge[] = {
/* Processor Home Agent */
@@ -421,6 +423,8 @@ static const struct pci_id_descr pci_dev_descr_ibridge[] = {
#endif
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0, 1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD2, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD3, 1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0, 1) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0, 1) },
@@ -504,17 +508,35 @@ static const struct pci_id_table pci_dev_descr_haswell_table[] = {
* DE processor:
* - 1 IMC
* - 2 DDR3 channels, 2 DPC per channel
+ * EP processor:
+ * - 1 or 2 IMC
+ * - 4 DDR4 channels, 3 DPC per channel
+ * EP 4S processor:
+ * - 2 IMC
+ * - 4 DDR4 channels, 3 DPC per channel
+ * EX processor:
+ * - 2 IMC
+ * - each IMC interfaces with a SMI 2 channel
+ * - each SMI channel interfaces with a scalable memory buffer
+ * - each scalable memory buffer supports 4 DDR3/DDR4 channels, 3 DPC
*/
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_VTD_MISC 0x6f28
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0 0x6fa0
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1 0x6f60
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA 0x6fa8
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_THERMAL 0x6f71
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TA 0x6f68
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_THERMAL 0x6f79
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_CBO_SAD0 0x6ffc
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_CBO_SAD1 0x6ffd
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD0 0x6faa
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD1 0x6fab
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD2 0x6fac
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD3 0x6fad
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD0 0x6f6a
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD1 0x6f6b
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD2 0x6f6c
+#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD3 0x6f6d
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_DDRIO0 0x6faf
static const struct pci_id_descr pci_dev_descr_broadwell[] = {
@@ -524,13 +546,23 @@ static const struct pci_id_descr pci_dev_descr_broadwell[] = {
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_CBO_SAD0, 0) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_CBO_SAD1, 0) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1, 1) },
+
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA, 0) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_THERMAL, 0) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD0, 0) },
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD1, 0) },
- { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD2, 0) },
- { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD3, 0) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD2, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD3, 1) },
+
{ PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_DDRIO0, 1) },
+
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TA, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_THERMAL, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD0, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD1, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD2, 1) },
+ { PCI_DESCR(PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD3, 1) },
};
static const struct pci_id_table pci_dev_descr_broadwell_table[] = {
@@ -559,7 +591,7 @@ static inline int numrank(enum type type, u32 mtr)
int ranks = (1 << RANK_CNT_BITS(mtr));
int max = 4;
- if (type == HASWELL)
+ if (type == HASWELL || type == BROADWELL)
max = 8;
if (ranks > max) {
@@ -909,6 +941,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
for (i = 0; i < NUM_CHANNELS; i++) {
u32 mtr;
+ if (!pvt->pci_tad[i])
+ continue;
for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
i, j, 0);
@@ -925,8 +959,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %Ld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
- pvt->sbridge_dev->mc, i, j,
+ edac_dbg(0, "mc#%d: ha %d channel %d, dimm %d, %lld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ pvt->sbridge_dev->mc, i/4, i%4, j,
size, npages,
banks, ranks, rows, cols);
@@ -946,8 +980,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
dimm->mtype = mtype;
dimm->edac_mode = mode;
snprintf(dimm->label, sizeof(dimm->label),
- "CPU_SrcID#%u_Channel#%u_DIMM#%u",
- pvt->sbridge_dev->source_id, i, j);
+ "CPU_SrcID#%u_Ha#%u_Chan#%u_DIMM#%u",
+ pvt->sbridge_dev->source_id, i/4, i%4, j);
}
}
}
@@ -1128,7 +1162,7 @@ static struct mem_ctl_info *get_mci_for_node_id(u8 node_id)
static int get_memory_error_data(struct mem_ctl_info *mci,
u64 addr,
- u8 *socket,
+ u8 *socket, u8 *ha,
long *channel_mask,
u8 *rank,
char **area_type, char *msg)
@@ -1141,7 +1175,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
int interleave_mode, shiftup = 0;
unsigned sad_interleave[pvt->info.max_interleave];
u32 reg, dram_rule;
- u8 ch_way, sck_way, pkg, sad_ha = 0;
+ u8 ch_way, sck_way, pkg, sad_ha = 0, ch_add = 0;
u32 tad_offset;
u32 rir_way;
u32 mb, gb;
@@ -1242,9 +1276,9 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
bits = GET_BITFIELD(addr, 7, 8) << 1;
bits |= GET_BITFIELD(addr, 9, 9);
} else
- bits = GET_BITFIELD(addr, 7, 9);
+ bits = GET_BITFIELD(addr, 6, 8);
- if (interleave_mode) {
+ if (interleave_mode == 0) {
/* interleave mode will XOR {8,7,6} with {18,17,16} */
idx = GET_BITFIELD(addr, 16, 18);
idx ^= bits;
@@ -1254,6 +1288,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
pkg = sad_pkg(pvt->info.interleave_pkg, reg, idx);
*socket = sad_pkg_socket(pkg);
sad_ha = sad_pkg_ha(pkg);
+ if (sad_ha)
+ ch_add = 4;
if (a7mode) {
/* MCChanShiftUpEnable */
@@ -1270,10 +1306,14 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
pkg = sad_pkg(pvt->info.interleave_pkg, reg, idx);
*socket = sad_pkg_socket(pkg);
sad_ha = sad_pkg_ha(pkg);
+ if (sad_ha)
+ ch_add = 4;
edac_dbg(0, "SAD interleave package: %d = CPU socket %d, HA %d\n",
idx, *socket, sad_ha);
}
+ *ha = sad_ha;
+
/*
* Move to the proper node structure, in order to access the
* right PCI registers
@@ -1346,7 +1386,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
}
*channel_mask = 1 << base_ch;
- pci_read_config_dword(pvt->pci_tad[base_ch],
+ pci_read_config_dword(pvt->pci_tad[ch_add + base_ch],
tad_ch_nilv_offset[n_tads],
&tad_offset);
@@ -1405,7 +1445,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
* Step 3) Decode rank
*/
for (n_rir = 0; n_rir < MAX_RIR_RANGES; n_rir++) {
- pci_read_config_dword(pvt->pci_tad[base_ch],
+ pci_read_config_dword(pvt->pci_tad[ch_add + base_ch],
rir_way_limit[n_rir],
&reg);
@@ -1435,7 +1475,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
idx = (ch_addr >> 13); /* FIXME: Datasheet says to shift by 15 */
idx %= 1 << rir_way;
- pci_read_config_dword(pvt->pci_tad[base_ch],
+ pci_read_config_dword(pvt->pci_tad[ch_add + base_ch],
rir_offset[n_rir][idx],
&reg);
*rank = RIR_RNK_TGT(reg);
@@ -1681,16 +1721,9 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
struct sbridge_dev *sbridge_dev)
{
struct sbridge_pvt *pvt = mci->pvt_info;
- struct pci_dev *pdev, *tmp;
+ struct pci_dev *pdev;
+ u8 saw_chan_mask = 0;
int i;
- bool mode_2ha = false;
-
- tmp = pci_get_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1, NULL);
- if (tmp) {
- mode_2ha = true;
- pci_dev_put(tmp);
- }
for (i = 0; i < sbridge_dev->n_devs; i++) {
pdev = sbridge_dev->pdev[i];
@@ -1706,26 +1739,21 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS:
pvt->pci_ras = pdev;
break;
- case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2:
- case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3:
- /* if we have 2 HAs active, channels 2 and 3
- * are in other device */
- if (mode_2ha)
- break;
- /* fall through */
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0:
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD1:
+ case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD2:
+ case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD3:
{
int id = pdev->device - PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TAD0;
pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
}
break;
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_2HA_DDRIO0:
pvt->pci_ddrio = pdev;
break;
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_1HA_DDRIO0:
- if (!mode_2ha)
- pvt->pci_ddrio = pdev;
+ pvt->pci_ddrio = pdev;
break;
case PCI_DEVICE_ID_INTEL_IBRIDGE_SAD:
pvt->pci_sad0 = pdev;
@@ -1741,13 +1769,12 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
break;
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0:
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD1:
+ case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD2:
+ case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD3:
{
- int id = pdev->device - PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0 + 2;
-
- /* we shouldn't have this device if we have just one
- * HA present */
- WARN_ON(!mode_2ha);
+ int id = pdev->device - PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA1_TAD0 + 4;
pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
}
break;
default:
@@ -1766,10 +1793,10 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci,
!pvt->pci_ta)
goto enodev;
- for (i = 0; i < NUM_CHANNELS; i++) {
- if (!pvt->pci_tad[i])
- goto enodev;
- }
+ if (saw_chan_mask != 0x0f && /* -EN */
+ saw_chan_mask != 0x33 && /* -EP */
+ saw_chan_mask != 0xff) /* -EX */
+ goto enodev;
return 0;
enodev:
@@ -1787,16 +1814,9 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci,
struct sbridge_dev *sbridge_dev)
{
struct sbridge_pvt *pvt = mci->pvt_info;
- struct pci_dev *pdev, *tmp;
+ struct pci_dev *pdev;
+ u8 saw_chan_mask = 0;
int i;
- bool mode_2ha = false;
-
- tmp = pci_get_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1, NULL);
- if (tmp) {
- mode_2ha = true;
- pci_dev_put(tmp);
- }
/* there's only one device per system; not tied to any bus */
if (pvt->info.pci_vtd == NULL)
@@ -1827,18 +1847,26 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci,
pvt->pci_ras = pdev;
break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD0:
- pvt->pci_tad[0] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD1:
- pvt->pci_tad[1] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD2:
- if (!mode_2ha)
- pvt->pci_tad[2] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3:
- if (!mode_2ha)
- pvt->pci_tad[3] = pdev;
+ {
+ int id = pdev->device - PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD0;
+
+ pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
+ }
+ break;
+ case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0:
+ case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD1:
+ case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD2:
+ case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD3:
+ {
+ int id = pdev->device - PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0 + 4;
+
+ pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
+ }
break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0:
pvt->pci_ddrio = pdev;
@@ -1849,14 +1877,6 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci,
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA:
pvt->pci_ha1_ta = pdev;
break;
- case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD0:
- if (mode_2ha)
- pvt->pci_tad[2] = pdev;
- break;
- case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD1:
- if (mode_2ha)
- pvt->pci_tad[3] = pdev;
- break;
default:
break;
}
@@ -1872,10 +1892,10 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci,
!pvt->pci_ras || !pvt->pci_ta || !pvt->info.pci_vtd)
goto enodev;
- for (i = 0; i < NUM_CHANNELS; i++) {
- if (!pvt->pci_tad[i])
- goto enodev;
- }
+ if (saw_chan_mask != 0x0f && /* -EN */
+ saw_chan_mask != 0x33 && /* -EP */
+ saw_chan_mask != 0xff) /* -EX */
+ goto enodev;
return 0;
enodev:
@@ -1888,6 +1908,7 @@ static int broadwell_mci_bind_devs(struct mem_ctl_info *mci,
{
struct sbridge_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev;
+ u8 saw_chan_mask = 0;
int i;
/* there's only one device per system; not tied to any bus */
@@ -1919,20 +1940,34 @@ static int broadwell_mci_bind_devs(struct mem_ctl_info *mci,
pvt->pci_ras = pdev;
break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD0:
- pvt->pci_tad[0] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD1:
- pvt->pci_tad[1] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD2:
- pvt->pci_tad[2] = pdev;
- break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD3:
- pvt->pci_tad[3] = pdev;
+ {
+ int id = pdev->device - PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TAD0;
+ pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
+ }
+ break;
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD0:
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD1:
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD2:
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD3:
+ {
+ int id = pdev->device - PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TAD0 + 4;
+ pvt->pci_tad[id] = pdev;
+ saw_chan_mask |= 1 << id;
+ }
break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_DDRIO0:
pvt->pci_ddrio = pdev;
break;
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1:
+ pvt->pci_ha1 = pdev;
+ break;
+ case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA1_TA:
+ pvt->pci_ha1_ta = pdev;
+ break;
default:
break;
}
@@ -1948,10 +1983,10 @@ static int broadwell_mci_bind_devs(struct mem_ctl_info *mci,
!pvt->pci_ras || !pvt->pci_ta || !pvt->info.pci_vtd)
goto enodev;
- for (i = 0; i < NUM_CHANNELS; i++) {
- if (!pvt->pci_tad[i])
- goto enodev;
- }
+ if (saw_chan_mask != 0x0f && /* -EN */
+ saw_chan_mask != 0x33 && /* -EP */
+ saw_chan_mask != 0xff) /* -EX */
+ goto enodev;
return 0;
enodev:
@@ -1986,11 +2021,11 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
u32 channel = GET_BITFIELD(m->status, 0, 3);
u32 optypenum = GET_BITFIELD(m->status, 4, 6);
long channel_mask, first_channel;
- u8 rank, socket;
+ u8 rank, socket, ha;
int rc, dimm;
char *area_type = NULL;
- if (pvt->info.type == IVY_BRIDGE)
+ if (pvt->info.type != SANDY_BRIDGE)
recoverable = true;
else
recoverable = GET_BITFIELD(m->status, 56, 56);
@@ -2048,7 +2083,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
if (!GET_BITFIELD(m->status, 58, 58))
return;
- rc = get_memory_error_data(mci, m->addr, &socket,
+ rc = get_memory_error_data(mci, m->addr, &socket, &ha,
&channel_mask, &rank, &area_type, msg);
if (rc < 0)
goto err_parsing;
@@ -2080,12 +2115,12 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
channel = first_channel;
snprintf(msg, sizeof(msg),
- "%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
+ "%s%s area:%s err_code:%04x:%04x socket:%d ha:%d channel_mask:%ld rank:%d",
overflow ? " OVERFLOW" : "",
(uncorrected_error && recoverable) ? " recoverable" : "",
area_type,
mscod, errcode,
- socket,
+ socket, ha,
channel_mask,
rank);
@@ -2099,7 +2134,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
/* Call the helper to output message */
edac_mc_handle_error(tp_event, mci, core_err_cnt,
m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
- channel, dimm, -1,
+ 4*ha+channel, dimm, -1,
optype, msg);
return;
err_parsing:
diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c
index e0f1cb3..ef76e5e 100644
--- a/drivers/firmware/dmi-sysfs.c
+++ b/drivers/firmware/dmi-sysfs.c
@@ -566,7 +566,6 @@ static struct kobj_type dmi_sysfs_entry_ktype = {
.default_attrs = dmi_sysfs_entry_attrs,
};
-static struct kobject *dmi_kobj;
static struct kset *dmi_kset;
/* Global count of all instances seen. Only for setup */
@@ -648,17 +647,20 @@ static void cleanup_entry_list(void)
static int __init dmi_sysfs_init(void)
{
- int error = -ENOMEM;
+ int error;
int val;
- /* Set up our directory */
- dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
- if (!dmi_kobj)
+ if (!dmi_kobj) {
+ pr_err("dmi-sysfs: dmi entry is absent.\n");
+ error = -ENODATA;
goto err;
+ }
dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
- if (!dmi_kset)
+ if (!dmi_kset) {
+ error = -ENOMEM;
goto err;
+ }
val = 0;
error = dmi_walk(dmi_sysfs_register_handle, &val);
@@ -675,7 +677,6 @@ static int __init dmi_sysfs_init(void)
err:
cleanup_entry_list();
kset_unregister(dmi_kset);
- kobject_put(dmi_kobj);
return error;
}
@@ -685,8 +686,6 @@ static void __exit dmi_sysfs_exit(void)
pr_debug("dmi-sysfs: unloading.\n");
cleanup_entry_list();
kset_unregister(dmi_kset);
- kobject_del(dmi_kobj);
- kobject_put(dmi_kobj);
}
module_init(dmi_sysfs_init);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 97b1616..ac1ce4a 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -10,6 +10,9 @@
#include <asm/dmi.h>
#include <asm/unaligned.h>
+struct kobject *dmi_kobj;
+EXPORT_SYMBOL_GPL(dmi_kobj);
+
/*
* DMI stands for "Desktop Management Interface". It is part
* of and an antecedent to, SMBIOS, which stands for System
@@ -20,6 +23,9 @@ static const char dmi_empty_string[] = " ";
static u32 dmi_ver __initdata;
static u32 dmi_len;
static u16 dmi_num;
+static u8 smbios_entry_point[32];
+static int smbios_entry_point_size;
+
/*
* Catch too early calls to dmi_check_system():
*/
@@ -80,18 +86,18 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s)
* We have to be cautious here. We have seen BIOSes with DMI pointers
* pointing to completely the wrong place for example
*/
-static void dmi_table(u8 *buf,
- void (*decode)(const struct dmi_header *, void *),
- void *private_data)
+static void dmi_decode_table(u8 *buf,
+ void (*decode)(const struct dmi_header *, void *),
+ void *private_data)
{
u8 *data = buf;
int i = 0;
/*
* Stop when we have seen all the items the table claimed to have
- * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run
- * off the end of the table (should never happen but sometimes does
- * on bogus implementations.)
+ * (SMBIOS < 3.0 only) OR we reach an end-of-table marker (SMBIOS
+ * >= 3.0 only) OR we run off the end of the table (should never
+ * happen but sometimes does on bogus implementations.)
*/
while ((!dmi_num || i < dmi_num) &&
(data - buf + sizeof(struct dmi_header)) <= dmi_len) {
@@ -108,15 +114,24 @@ static void dmi_table(u8 *buf,
if (data - buf < dmi_len - 1)
decode(dm, private_data);
+ data += 2;
+ i++;
+
/*
* 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
+ * For tables behind a 64-bit entry point, we have no item
+ * count and no exact table length, so stop on end-of-table
+ * marker. For tables behind a 32-bit entry point, we have
+ * seen OEM structures behind the end-of-table marker on
+ * some systems, so don't trust it.
*/
- if (dm->type == DMI_ENTRY_END_OF_TABLE)
+ if (!dmi_num && dm->type == DMI_ENTRY_END_OF_TABLE)
break;
-
- data += 2;
- i++;
}
+
+ /* Trim DMI table length if needed */
+ if (dmi_len > data - buf)
+ dmi_len = data - buf;
}
static phys_addr_t dmi_base;
@@ -125,16 +140,17 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
void *))
{
u8 *buf;
+ u32 orig_dmi_len = dmi_len;
- buf = dmi_early_remap(dmi_base, dmi_len);
+ buf = dmi_early_remap(dmi_base, orig_dmi_len);
if (buf == NULL)
return -1;
- dmi_table(buf, decode, NULL);
+ dmi_decode_table(buf, decode, NULL);
add_device_randomness(buf, dmi_len);
- dmi_early_unmap(buf, dmi_len);
+ dmi_early_unmap(buf, orig_dmi_len);
return 0;
}
@@ -478,17 +494,19 @@ static int __init dmi_present(const u8 *buf)
if (memcmp(buf, "_SM_", 4) == 0 &&
buf[5] < 32 && dmi_checksum(buf, buf[5])) {
smbios_ver = get_unaligned_be16(buf + 6);
+ smbios_entry_point_size = buf[5];
+ memcpy(smbios_entry_point, buf, smbios_entry_point_size);
/* Some BIOS report weird SMBIOS version, fix that up */
switch (smbios_ver) {
case 0x021F:
case 0x0221:
- pr_debug("SMBIOS version fixup(2.%d->2.%d)\n",
+ pr_debug("SMBIOS version fixup (2.%d->2.%d)\n",
smbios_ver & 0xFF, 3);
smbios_ver = 0x0203;
break;
case 0x0233:
- pr_debug("SMBIOS version fixup(2.%d->2.%d)\n", 51, 6);
+ pr_debug("SMBIOS version fixup (2.%d->2.%d)\n", 51, 6);
smbios_ver = 0x0206;
break;
}
@@ -512,6 +530,9 @@ static int __init dmi_present(const u8 *buf)
pr_info("SMBIOS %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
} else {
+ smbios_entry_point_size = 15;
+ memcpy(smbios_entry_point, buf,
+ smbios_entry_point_size);
pr_info("Legacy DMI %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
}
@@ -533,11 +554,12 @@ static int __init dmi_smbios3_present(const u8 *buf)
{
if (memcmp(buf, "_SM3_", 5) == 0 &&
buf[6] < 32 && dmi_checksum(buf, buf[6])) {
- dmi_ver = get_unaligned_be32(buf + 6);
- dmi_ver &= 0xFFFFFF;
+ dmi_ver = get_unaligned_be32(buf + 6) & 0xFFFFFF;
dmi_num = 0; /* No longer specified */
dmi_len = get_unaligned_le32(buf + 12);
dmi_base = get_unaligned_le64(buf + 16);
+ smbios_entry_point_size = buf[6];
+ memcpy(smbios_entry_point, buf, smbios_entry_point_size);
if (dmi_walk_early(dmi_decode) == 0) {
pr_info("SMBIOS %d.%d.%d present.\n",
@@ -629,6 +651,71 @@ void __init dmi_scan_machine(void)
dmi_initialized = 1;
}
+static ssize_t raw_table_read(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t pos, size_t count)
+{
+ memcpy(buf, attr->private + pos, count);
+ return count;
+}
+
+static BIN_ATTR(smbios_entry_point, S_IRUSR, raw_table_read, NULL, 0);
+static BIN_ATTR(DMI, S_IRUSR, raw_table_read, NULL, 0);
+
+static int __init dmi_init(void)
+{
+ struct kobject *tables_kobj;
+ u8 *dmi_table;
+ int ret = -ENOMEM;
+
+ if (!dmi_available) {
+ ret = -ENODATA;
+ goto err;
+ }
+
+ /*
+ * Set up dmi directory at /sys/firmware/dmi. This entry should stay
+ * even after farther error, as it can be used by other modules like
+ * dmi-sysfs.
+ */
+ dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
+ if (!dmi_kobj)
+ goto err;
+
+ tables_kobj = kobject_create_and_add("tables", dmi_kobj);
+ if (!tables_kobj)
+ goto err;
+
+ dmi_table = dmi_remap(dmi_base, dmi_len);
+ if (!dmi_table)
+ goto err_tables;
+
+ bin_attr_smbios_entry_point.size = smbios_entry_point_size;
+ bin_attr_smbios_entry_point.private = smbios_entry_point;
+ ret = sysfs_create_bin_file(tables_kobj, &bin_attr_smbios_entry_point);
+ if (ret)
+ goto err_unmap;
+
+ bin_attr_DMI.size = dmi_len;
+ bin_attr_DMI.private = dmi_table;
+ ret = sysfs_create_bin_file(tables_kobj, &bin_attr_DMI);
+ if (!ret)
+ return 0;
+
+ sysfs_remove_bin_file(tables_kobj,
+ &bin_attr_smbios_entry_point);
+ err_unmap:
+ dmi_unmap(dmi_table);
+ err_tables:
+ kobject_del(tables_kobj);
+ kobject_put(tables_kobj);
+ err:
+ pr_err("dmi: Firmware registration failed.\n");
+
+ return ret;
+}
+subsys_initcall(dmi_init);
+
/**
* dmi_set_dump_stack_arch_desc - set arch description for dump_stack()
*
@@ -897,7 +984,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *),
if (buf == NULL)
return -1;
- dmi_table(buf, decode, private_data);
+ dmi_decode_table(buf, decode, private_data);
dmi_unmap(buf);
return 0;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 773d1d2..3aaed09 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -6479,6 +6479,9 @@ enum skl_disp_power_wells {
#define AUDIO_CP_READY(trans) ((1 << 1) << ((trans) * 4))
#define AUDIO_ELD_VALID(trans) ((1 << 0) << ((trans) * 4))
+#define HSW_AUD_CHICKENBIT 0x65f10
+#define SKL_AUD_CODEC_WAKE_SIGNAL (1 << 15)
+
/* HSW Power Wells */
#define HSW_PWR_WELL_BIOS 0x45400 /* CTL1 */
#define HSW_PWR_WELL_DRIVER 0x45404 /* CTL2 */
diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c
index 2396cc7..ef34257 100644
--- a/drivers/gpu/drm/i915/intel_audio.c
+++ b/drivers/gpu/drm/i915/intel_audio.c
@@ -475,6 +475,32 @@ static void i915_audio_component_put_power(struct device *dev)
intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO);
}
+static void i915_audio_component_codec_wake_override(struct device *dev,
+ bool enable)
+{
+ struct drm_i915_private *dev_priv = dev_to_i915(dev);
+ u32 tmp;
+
+ if (!IS_SKYLAKE(dev_priv))
+ return;
+
+ /*
+ * Enable/disable generating the codec wake signal, overriding the
+ * internal logic to generate the codec wake to controller.
+ */
+ tmp = I915_READ(HSW_AUD_CHICKENBIT);
+ tmp &= ~SKL_AUD_CODEC_WAKE_SIGNAL;
+ I915_WRITE(HSW_AUD_CHICKENBIT, tmp);
+ usleep_range(1000, 1500);
+
+ if (enable) {
+ tmp = I915_READ(HSW_AUD_CHICKENBIT);
+ tmp |= SKL_AUD_CODEC_WAKE_SIGNAL;
+ I915_WRITE(HSW_AUD_CHICKENBIT, tmp);
+ usleep_range(1000, 1500);
+ }
+}
+
/* Get CDCLK in kHz */
static int i915_audio_component_get_cdclk_freq(struct device *dev)
{
@@ -495,6 +521,7 @@ static const struct i915_audio_component_ops i915_audio_component_ops = {
.owner = THIS_MODULE,
.get_power = i915_audio_component_get_power,
.put_power = i915_audio_component_put_power,
+ .codec_wake_override = i915_audio_component_codec_wake_override,
.get_cdclk_freq = i915_audio_component_get_cdclk_freq,
};
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 580dbf0..e370804 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -521,7 +521,7 @@ static int pca_init(struct i2c_adapter *adap)
pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
}
- udelay(500); /* 500 us for oscilator to stabilise */
+ udelay(500); /* 500 us for oscillator to stabilise */
return 0;
}
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f1c1c4..35ac237 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -392,6 +392,16 @@ config I2C_BCM_KONA
If you do not need KONA I2C interface, say N.
+config I2C_BRCMSTB
+ tristate "BRCM Settop I2C controller"
+ depends on ARCH_BRCMSTB || COMPILE_TEST
+ default y
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Broadcom Settop SoCs.
+
+ If you do not need I2C interface, say N.
+
config I2C_BLACKFIN_TWI
tristate "Blackfin TWI I2C support"
depends on BLACKFIN
@@ -419,7 +429,7 @@ config I2C_CADENCE
config I2C_CBUS_GPIO
tristate "CBUS I2C driver"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
Support for CBUS access using I2C API. Mostly relevant for Nokia
Internet Tablets (770, N800 and N810).
@@ -525,7 +535,7 @@ config I2C_EXYNOS5
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select I2C_ALGOBIT
help
This is a very simple bitbanging I2C driver utilizing the
@@ -620,6 +630,15 @@ config I2C_MPC
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
+config I2C_MT65XX
+ tristate "MediaTek I2C adapter"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ help
+ This selects the MediaTek(R) Integrated Inter Circuit bus driver
+ for MT65xx and MT81xx.
+ If you want to use MediaTek(R) I2C interface, say Y or M here.
+ If unsure, say N.
+
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
depends on MV64X60 || PLAT_ORION || ARCH_SUNXI
@@ -1110,6 +1129,15 @@ config I2C_CROS_EC_TUNNEL
connected there. This will work whatever the interface used to
talk to the EC (SPI, I2C or LPC).
+config I2C_XGENE_SLIMPRO
+ tristate "APM X-Gene SoC I2C SLIMpro devices support"
+ depends on ARCH_XGENE && MAILBOX
+ help
+ Enable I2C bus access using the APM X-Gene SoC SLIMpro
+ co-processor. The I2C device access the I2C bus via the X-Gene
+ to SLIMpro (On chip coprocessor) mailbox mechanism.
+ If unsure, say N.
+
config SCx200_ACB
tristate "Geode ACCESS.bus support"
depends on X86_32 && PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index cdf941d..e5f537c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
+obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
@@ -105,11 +106,13 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
# Other I2C/SMBus bus drivers
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
+obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
+obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index ff23d1b..1c758cd 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -41,29 +41,41 @@
/* AT91 TWI register definitions */
#define AT91_TWI_CR 0x0000 /* Control Register */
-#define AT91_TWI_START 0x0001 /* Send a Start Condition */
-#define AT91_TWI_STOP 0x0002 /* Send a Stop Condition */
-#define AT91_TWI_MSEN 0x0004 /* Master Transfer Enable */
-#define AT91_TWI_SVDIS 0x0020 /* Slave Transfer Disable */
-#define AT91_TWI_QUICK 0x0040 /* SMBus quick command */
-#define AT91_TWI_SWRST 0x0080 /* Software Reset */
+#define AT91_TWI_START BIT(0) /* Send a Start Condition */
+#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
+#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
+#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
+#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
+#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
+#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
+#define AT91_TWI_SWRST BIT(7) /* Software Reset */
+#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
+#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
+#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
+#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
+#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
+#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
+#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
-#define AT91_TWI_MREAD 0x1000 /* Master Read Direction */
+#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
#define AT91_TWI_SR 0x0020 /* Status Register */
-#define AT91_TWI_TXCOMP 0x0001 /* Transmission Complete */
-#define AT91_TWI_RXRDY 0x0002 /* Receive Holding Register Ready */
-#define AT91_TWI_TXRDY 0x0004 /* Transmit Holding Register Ready */
+#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
+#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
+#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
+#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
+#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
+#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
+#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
-#define AT91_TWI_OVRE 0x0040 /* Overrun Error */
-#define AT91_TWI_UNRE 0x0080 /* Underrun Error */
-#define AT91_TWI_NACK 0x0100 /* Not Acknowledged */
+#define AT91_TWI_INT_MASK \
+ (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK)
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
@@ -71,17 +83,40 @@
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
+#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
+#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
+#define AT91_TWI_ACR_DIR BIT(8)
+
+#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
+#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
+#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
+#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
+#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
+#define AT91_TWI_ONE_DATA 0x0
+#define AT91_TWI_TWO_DATA 0x1
+#define AT91_TWI_FOUR_DATA 0x2
+
+#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
+
+#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
+#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
+#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
+#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
+
+#define AT91_TWI_VER 0x00fc /* Version Register */
+
struct at91_twi_pdata {
unsigned clk_max_div;
unsigned clk_offset;
bool has_unre_flag;
+ bool has_alt_cmd;
struct at_dma_slave dma_slave;
};
struct at91_twi_dma {
struct dma_chan *chan_rx;
struct dma_chan *chan_tx;
- struct scatterlist sg;
+ struct scatterlist sg[2];
struct dma_async_tx_descriptor *data_desc;
enum dma_data_direction direction;
bool buf_mapped;
@@ -104,6 +139,7 @@ struct at91_twi_dev {
struct at91_twi_pdata *pdata;
bool use_dma;
bool recv_len_abort;
+ u32 fifo_size;
struct at91_twi_dma dma;
};
@@ -119,13 +155,12 @@ static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
{
- at91_twi_write(dev, AT91_TWI_IDR,
- AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY);
+ at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
}
static void at91_twi_irq_save(struct at91_twi_dev *dev)
{
- dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & 0x7;
+ dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
at91_disable_twi_interrupts(dev);
}
@@ -138,6 +173,9 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
{
at91_disable_twi_interrupts(dev);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
+ /* FIFO should be enabled immediately after the software reset */
+ if (dev->fifo_size)
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS);
at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg);
@@ -184,7 +222,7 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
dma->xfer_in_progress = false;
}
if (dma->buf_mapped) {
- dma_unmap_single(dev->dev, sg_dma_address(&dma->sg),
+ dma_unmap_single(dev->dev, sg_dma_address(&dma->sg[0]),
dev->buf_len, dma->direction);
dma->buf_mapped = false;
}
@@ -194,14 +232,16 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
{
- if (dev->buf_len <= 0)
+ if (!dev->buf_len)
return;
- at91_twi_write(dev, AT91_TWI_THR, *dev->buf);
+ /* 8bit write works with and without FIFO */
+ writeb_relaxed(*dev->buf, dev->base + AT91_TWI_THR);
/* send stop when last byte has been written */
if (--dev->buf_len == 0)
- at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
+ if (!dev->pdata->has_alt_cmd)
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
@@ -212,10 +252,19 @@ static void at91_twi_write_data_dma_callback(void *data)
{
struct at91_twi_dev *dev = (struct at91_twi_dev *)data;
- dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg),
+ dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]),
dev->buf_len, DMA_TO_DEVICE);
- at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
+ /*
+ * When this callback is called, THR/TX FIFO is likely not to be empty
+ * yet. So we have to wait for TXCOMP or NACK bits to be set into the
+ * Status Register to be sure that the STOP bit has been sent and the
+ * transfer is completed. The NACK interrupt has already been enabled,
+ * we just have to enable TXCOMP one.
+ */
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
+ if (!dev->pdata->has_alt_cmd)
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
}
static void at91_twi_write_data_dma(struct at91_twi_dev *dev)
@@ -224,8 +273,9 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev)
struct dma_async_tx_descriptor *txdesc;
struct at91_twi_dma *dma = &dev->dma;
struct dma_chan *chan_tx = dma->chan_tx;
+ unsigned int sg_len = 1;
- if (dev->buf_len <= 0)
+ if (!dev->buf_len)
return;
dma->direction = DMA_TO_DEVICE;
@@ -239,10 +289,43 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev)
}
dma->buf_mapped = true;
at91_twi_irq_restore(dev);
- sg_dma_len(&dma->sg) = dev->buf_len;
- sg_dma_address(&dma->sg) = dma_addr;
- txdesc = dmaengine_prep_slave_sg(chan_tx, &dma->sg, 1, DMA_MEM_TO_DEV,
+ if (dev->fifo_size) {
+ size_t part1_len, part2_len;
+ struct scatterlist *sg;
+ unsigned fifo_mr;
+
+ sg_len = 0;
+
+ part1_len = dev->buf_len & ~0x3;
+ if (part1_len) {
+ sg = &dma->sg[sg_len++];
+ sg_dma_len(sg) = part1_len;
+ sg_dma_address(sg) = dma_addr;
+ }
+
+ part2_len = dev->buf_len & 0x3;
+ if (part2_len) {
+ sg = &dma->sg[sg_len++];
+ sg_dma_len(sg) = part2_len;
+ sg_dma_address(sg) = dma_addr + part1_len;
+ }
+
+ /*
+ * DMA controller is triggered when at least 4 data can be
+ * written into the TX FIFO
+ */
+ fifo_mr = at91_twi_read(dev, AT91_TWI_FMR);
+ fifo_mr &= ~AT91_TWI_FMR_TXRDYM_MASK;
+ fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_FOUR_DATA);
+ at91_twi_write(dev, AT91_TWI_FMR, fifo_mr);
+ } else {
+ sg_dma_len(&dma->sg[0]) = dev->buf_len;
+ sg_dma_address(&dma->sg[0]) = dma_addr;
+ }
+
+ txdesc = dmaengine_prep_slave_sg(chan_tx, dma->sg, sg_len,
+ DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!txdesc) {
dev_err(dev->dev, "dma prep slave sg failed\n");
@@ -264,10 +347,11 @@ error:
static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
{
- if (dev->buf_len <= 0)
+ if (!dev->buf_len)
return;
- *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff;
+ /* 8bit read works with and without FIFO */
+ *dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR);
--dev->buf_len;
/* return if aborting, we only needed to read RHR to clear RXRDY*/
@@ -291,7 +375,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
}
/* send stop if second but last byte has been read */
- if (dev->buf_len == 1)
+ if (!dev->pdata->has_alt_cmd && dev->buf_len == 1)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
@@ -302,14 +386,18 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
static void at91_twi_read_data_dma_callback(void *data)
{
struct at91_twi_dev *dev = (struct at91_twi_dev *)data;
+ unsigned ier = AT91_TWI_TXCOMP;
- dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg),
+ dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]),
dev->buf_len, DMA_FROM_DEVICE);
- /* The last two bytes have to be read without using dma */
- dev->buf += dev->buf_len - 2;
- dev->buf_len = 2;
- at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_RXRDY);
+ if (!dev->pdata->has_alt_cmd) {
+ /* The last two bytes have to be read without using dma */
+ dev->buf += dev->buf_len - 2;
+ dev->buf_len = 2;
+ ier |= AT91_TWI_RXRDY;
+ }
+ at91_twi_write(dev, AT91_TWI_IER, ier);
}
static void at91_twi_read_data_dma(struct at91_twi_dev *dev)
@@ -318,23 +406,38 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev)
struct dma_async_tx_descriptor *rxdesc;
struct at91_twi_dma *dma = &dev->dma;
struct dma_chan *chan_rx = dma->chan_rx;
+ size_t buf_len;
+ buf_len = (dev->pdata->has_alt_cmd) ? dev->buf_len : dev->buf_len - 2;
dma->direction = DMA_FROM_DEVICE;
/* Keep in mind that we won't use dma to read the last two bytes */
at91_twi_irq_save(dev);
- dma_addr = dma_map_single(dev->dev, dev->buf, dev->buf_len - 2,
- DMA_FROM_DEVICE);
+ dma_addr = dma_map_single(dev->dev, dev->buf, buf_len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev->dev, dma_addr)) {
dev_err(dev->dev, "dma map failed\n");
return;
}
dma->buf_mapped = true;
at91_twi_irq_restore(dev);
- dma->sg.dma_address = dma_addr;
- sg_dma_len(&dma->sg) = dev->buf_len - 2;
- rxdesc = dmaengine_prep_slave_sg(chan_rx, &dma->sg, 1, DMA_DEV_TO_MEM,
+ if (dev->fifo_size && IS_ALIGNED(buf_len, 4)) {
+ unsigned fifo_mr;
+
+ /*
+ * DMA controller is triggered when at least 4 data can be
+ * read from the RX FIFO
+ */
+ fifo_mr = at91_twi_read(dev, AT91_TWI_FMR);
+ fifo_mr &= ~AT91_TWI_FMR_RXRDYM_MASK;
+ fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_FOUR_DATA);
+ at91_twi_write(dev, AT91_TWI_FMR, fifo_mr);
+ }
+
+ sg_dma_len(&dma->sg[0]) = buf_len;
+ sg_dma_address(&dma->sg[0]) = dma_addr;
+
+ rxdesc = dmaengine_prep_slave_sg(chan_rx, dma->sg, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!rxdesc) {
dev_err(dev->dev, "dma prep slave sg failed\n");
@@ -370,7 +473,7 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
/* catch error flags */
dev->transfer_status |= status;
- if (irqstatus & AT91_TWI_TXCOMP) {
+ if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) {
at91_disable_twi_interrupts(dev);
complete(&dev->cmd_complete);
}
@@ -383,6 +486,50 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
int ret;
unsigned long time_left;
bool has_unre_flag = dev->pdata->has_unre_flag;
+ bool has_alt_cmd = dev->pdata->has_alt_cmd;
+
+ /*
+ * WARNING: the TXCOMP bit in the Status Register is NOT a clear on
+ * read flag but shows the state of the transmission at the time the
+ * Status Register is read. According to the programmer datasheet,
+ * TXCOMP is set when both holding register and internal shifter are
+ * empty and STOP condition has been sent.
+ * Consequently, we should enable NACK interrupt rather than TXCOMP to
+ * detect transmission failure.
+ * Indeed let's take the case of an i2c write command using DMA.
+ * Whenever the slave doesn't acknowledge a byte, the LOCK, NACK and
+ * TXCOMP bits are set together into the Status Register.
+ * LOCK is a clear on write bit, which is set to prevent the DMA
+ * controller from sending new data on the i2c bus after a NACK
+ * condition has happened. Once locked, this i2c peripheral stops
+ * triggering the DMA controller for new data but it is more than
+ * likely that a new DMA transaction is already in progress, writing
+ * into the Transmit Holding Register. Since the peripheral is locked,
+ * these new data won't be sent to the i2c bus but they will remain
+ * into the Transmit Holding Register, so TXCOMP bit is cleared.
+ * Then when the interrupt handler is called, the Status Register is
+ * read: the TXCOMP bit is clear but NACK bit is still set. The driver
+ * manage the error properly, without waiting for timeout.
+ * This case can be reproduced easyly when writing into an at24 eeprom.
+ *
+ * Besides, the TXCOMP bit is already set before the i2c transaction
+ * has been started. For read transactions, this bit is cleared when
+ * writing the START bit into the Control Register. So the
+ * corresponding interrupt can safely be enabled just after.
+ * However for write transactions managed by the CPU, we first write
+ * into THR, so TXCOMP is cleared. Then we can safely enable TXCOMP
+ * interrupt. If TXCOMP interrupt were enabled before writing into THR,
+ * the interrupt handler would be called immediately and the i2c command
+ * would be reported as completed.
+ * Also when a write transaction is managed by the DMA controller,
+ * enabling the TXCOMP interrupt in this function may lead to a race
+ * condition since we don't know whether the TXCOMP interrupt is enabled
+ * before or after the DMA has started to write into THR. So the TXCOMP
+ * interrupt is enabled later by at91_twi_write_data_dma_callback().
+ * Immediately after in that DMA callback, if the alternative command
+ * mode is not used, we still need to send the STOP condition manually
+ * writing the corresponding bit into the Control Register.
+ */
dev_dbg(dev->dev, "transfer: %s %d bytes.\n",
(dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
@@ -390,6 +537,21 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
reinit_completion(&dev->cmd_complete);
dev->transfer_status = 0;
+ if (dev->fifo_size) {
+ unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR);
+
+ /* Reset FIFO mode register */
+ fifo_mr &= ~(AT91_TWI_FMR_TXRDYM_MASK |
+ AT91_TWI_FMR_RXRDYM_MASK);
+ fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_ONE_DATA);
+ fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_ONE_DATA);
+ at91_twi_write(dev, AT91_TWI_FMR, fifo_mr);
+
+ /* Flush FIFOs */
+ at91_twi_write(dev, AT91_TWI_CR,
+ AT91_TWI_THRCLR | AT91_TWI_RHRCLR);
+ }
+
if (!dev->buf_len) {
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK);
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
@@ -402,44 +564,45 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
}
/* if only one byte is to be read, immediately stop transfer */
- if (dev->buf_len <= 1 && !(dev->msg->flags & I2C_M_RECV_LEN))
+ if (!has_alt_cmd && dev->buf_len <= 1 &&
+ !(dev->msg->flags & I2C_M_RECV_LEN))
start_flags |= AT91_TWI_STOP;
at91_twi_write(dev, AT91_TWI_CR, start_flags);
/*
- * When using dma, the last byte has to be read manually in
- * order to not send the stop command too late and then
- * to receive extra data. In practice, there are some issues
- * if you use the dma to read n-1 bytes because of latency.
+ * When using dma without alternative command mode, the last
+ * byte has to be read manually in order to not send the stop
+ * command too late and then to receive extra data.
+ * In practice, there are some issues if you use the dma to
+ * read n-1 bytes because of latency.
* Reading n-2 bytes with dma and the two last ones manually
* seems to be the best solution.
*/
if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) {
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK);
at91_twi_read_data_dma(dev);
- /*
- * It is important to enable TXCOMP irq here because
- * doing it only when transferring the last two bytes
- * will mask NACK errors since TXCOMP is set when a
- * NACK occurs.
- */
- at91_twi_write(dev, AT91_TWI_IER,
- AT91_TWI_TXCOMP);
- } else
+ } else {
at91_twi_write(dev, AT91_TWI_IER,
- AT91_TWI_TXCOMP | AT91_TWI_RXRDY);
+ AT91_TWI_TXCOMP |
+ AT91_TWI_NACK |
+ AT91_TWI_RXRDY);
+ }
} else {
if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) {
+ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK);
at91_twi_write_data_dma(dev);
- at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
} else {
at91_twi_write_next_byte(dev);
at91_twi_write(dev, AT91_TWI_IER,
- AT91_TWI_TXCOMP | AT91_TWI_TXRDY);
+ AT91_TWI_TXCOMP |
+ AT91_TWI_NACK |
+ AT91_TWI_TXRDY);
}
}
time_left = wait_for_completion_timeout(&dev->cmd_complete,
dev->adapter.timeout);
if (time_left == 0) {
+ dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR);
dev_err(dev->dev, "controller timed out\n");
at91_init_twi_bus(dev);
ret = -ETIMEDOUT;
@@ -460,6 +623,12 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
ret = -EIO;
goto error;
}
+ if ((has_alt_cmd || dev->fifo_size) &&
+ (dev->transfer_status & AT91_TWI_LOCK)) {
+ dev_err(dev->dev, "tx locked\n");
+ ret = -EIO;
+ goto error;
+ }
if (dev->recv_len_abort) {
dev_err(dev->dev, "invalid smbus block length recvd\n");
ret = -EPROTO;
@@ -471,7 +640,15 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
return 0;
error:
+ /* first stop DMA transfer if still in progress */
at91_twi_dma_cleanup(dev);
+ /* then flush THR/FIFO and unlock TX if locked */
+ if ((has_alt_cmd || dev->fifo_size) &&
+ (dev->transfer_status & AT91_TWI_LOCK)) {
+ dev_dbg(dev->dev, "unlock tx\n");
+ at91_twi_write(dev, AT91_TWI_CR,
+ AT91_TWI_THRCLR | AT91_TWI_LOCKCLR);
+ }
return ret;
}
@@ -481,6 +658,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
int ret;
unsigned int_addr_flag = 0;
struct i2c_msg *m_start = msg;
+ bool is_read, use_alt_cmd = false;
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
@@ -503,8 +681,23 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
at91_twi_write(dev, AT91_TWI_IADR, internal_address);
}
- at91_twi_write(dev, AT91_TWI_MMR, (m_start->addr << 16) | int_addr_flag
- | ((m_start->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0));
+ is_read = (m_start->flags & I2C_M_RD);
+ if (dev->pdata->has_alt_cmd) {
+ if (m_start->len > 0) {
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMEN);
+ at91_twi_write(dev, AT91_TWI_ACR,
+ AT91_TWI_ACR_DATAL(m_start->len) |
+ ((is_read) ? AT91_TWI_ACR_DIR : 0));
+ use_alt_cmd = true;
+ } else {
+ at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMDIS);
+ }
+ }
+
+ at91_twi_write(dev, AT91_TWI_MMR,
+ (m_start->addr << 16) |
+ int_addr_flag |
+ ((!use_alt_cmd && is_read) ? AT91_TWI_MREAD : 0));
dev->buf_len = m_start->len;
dev->buf = m_start->buf;
@@ -545,30 +738,35 @@ static struct at91_twi_pdata at91rm9200_config = {
.clk_max_div = 5,
.clk_offset = 3,
.has_unre_flag = true,
+ .has_alt_cmd = false,
};
static struct at91_twi_pdata at91sam9261_config = {
.clk_max_div = 5,
.clk_offset = 4,
.has_unre_flag = false,
+ .has_alt_cmd = false,
};
static struct at91_twi_pdata at91sam9260_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
+ .has_alt_cmd = false,
};
static struct at91_twi_pdata at91sam9g20_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
+ .has_alt_cmd = false,
};
static struct at91_twi_pdata at91sam9g10_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
+ .has_alt_cmd = false,
};
static const struct platform_device_id at91_twi_devtypes[] = {
@@ -597,6 +795,14 @@ static struct at91_twi_pdata at91sam9x5_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
+ .has_alt_cmd = false,
+};
+
+static struct at91_twi_pdata sama5d2_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = true,
+ .has_alt_cmd = true,
};
static const struct of_device_id atmel_twi_dt_ids[] = {
@@ -619,6 +825,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
+ .compatible = "atmel,sama5d2-i2c",
+ .data = &sama5d2_config,
+ }, {
/* sentinel */
}
};
@@ -630,13 +839,32 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
int ret = 0;
struct dma_slave_config slave_config;
struct at91_twi_dma *dma = &dev->dma;
+ enum dma_slave_buswidth addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+ /*
+ * The actual width of the access will be chosen in
+ * dmaengine_prep_slave_sg():
+ * for each buffer in the scatter-gather list, if its size is aligned
+ * to addr_width then addr_width accesses will be performed to transfer
+ * the buffer. On the other hand, if the buffer size is not aligned to
+ * addr_width then the buffer is transferred using single byte accesses.
+ * Please refer to the Atmel eXtended DMA controller driver.
+ * When FIFOs are used, the TXRDYM threshold can always be set to
+ * trigger the XDMAC when at least 4 data can be written into the TX
+ * FIFO, even if single byte accesses are performed.
+ * However the RXRDYM threshold must be set to fit the access width,
+ * deduced from buffer length, so the XDMAC is triggered properly to
+ * read data from the RX FIFO.
+ */
+ if (dev->fifo_size)
+ addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
memset(&slave_config, 0, sizeof(slave_config));
slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR;
- slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.src_addr_width = addr_width;
slave_config.src_maxburst = 1;
slave_config.dst_addr = (dma_addr_t)phy_addr + AT91_TWI_THR;
- slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.dst_addr_width = addr_width;
slave_config.dst_maxburst = 1;
slave_config.device_fc = false;
@@ -668,7 +896,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
goto error;
}
- sg_init_table(&dma->sg, 1);
+ sg_init_table(dma->sg, 2);
dma->buf_mapped = false;
dma->xfer_in_progress = false;
dev->use_dma = true;
@@ -754,6 +982,11 @@ static int at91_twi_probe(struct platform_device *pdev)
return rc;
}
+ if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
+ &dev->fifo_size)) {
+ dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
+ }
+
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
&bus_clk_rate);
if (rc)
@@ -790,7 +1023,8 @@ static int at91_twi_probe(struct platform_device *pdev)
return rc;
}
- dev_info(dev->dev, "AT91 i2c bus driver.\n");
+ dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
+ at91_twi_read(dev, AT91_TWI_VER));
return 0;
}
diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c
index 32d8834..c335cc7 100644
--- a/drivers/i2c/busses/i2c-axxia.c
+++ b/drivers/i2c/busses/i2c-axxia.c
@@ -42,6 +42,10 @@
#define IBML_LOW_SEXT 0x18
#define TIMER_CLOCK_DIV 0x1c
#define I2C_BUS_MONITOR 0x20
+#define BM_SDAC BIT(3)
+#define BM_SCLC BIT(2)
+#define BM_SDAS BIT(1)
+#define BM_SCLS BIT(0)
#define SOFT_RESET 0x24
#define MST_COMMAND 0x28
#define CMD_BUSY (1<<3)
@@ -394,6 +398,9 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
if (time_left == 0)
idev->msg_err = -ETIMEDOUT;
+ if (idev->msg_err == -ETIMEDOUT)
+ i2c_recover_bus(&idev->adapter);
+
if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO)
axxia_i2c_init(idev);
@@ -437,6 +444,39 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
return ret ? : i;
}
+static int axxia_i2c_get_scl(struct i2c_adapter *adap)
+{
+ struct axxia_i2c_dev *idev = i2c_get_adapdata(adap);
+
+ return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SCLS);
+}
+
+static void axxia_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+ struct axxia_i2c_dev *idev = i2c_get_adapdata(adap);
+ u32 tmp;
+
+ /* Preserve SDA Control */
+ tmp = readl(idev->base + I2C_BUS_MONITOR) & BM_SDAC;
+ if (!val)
+ tmp |= BM_SCLC;
+ writel(tmp, idev->base + I2C_BUS_MONITOR);
+}
+
+static int axxia_i2c_get_sda(struct i2c_adapter *adap)
+{
+ struct axxia_i2c_dev *idev = i2c_get_adapdata(adap);
+
+ return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SDAS);
+}
+
+static struct i2c_bus_recovery_info axxia_i2c_recovery_info = {
+ .recover_bus = i2c_generic_scl_recovery,
+ .get_scl = axxia_i2c_get_scl,
+ .set_scl = axxia_i2c_set_scl,
+ .get_sda = axxia_i2c_get_sda,
+};
+
static u32 axxia_i2c_func(struct i2c_adapter *adap)
{
u32 caps = (I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
@@ -511,6 +551,7 @@ static int axxia_i2c_probe(struct platform_device *pdev)
strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
idev->adapter.owner = THIS_MODULE;
idev->adapter.algo = &axxia_i2c_algo;
+ idev->adapter.bus_recovery_info = &axxia_i2c_recovery_info;
idev->adapter.quirks = &axxia_i2c_quirks;
idev->adapter.dev.parent = &pdev->dev;
idev->adapter.dev.of_node = pdev->dev.of_node;
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index f9f2c20..0419f52 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -91,6 +91,7 @@ struct bcm_iproc_i2c_dev {
void __iomem *base;
struct i2c_adapter adapter;
+ unsigned int bus_speed;
struct completion done;
int xfer_is_done;
@@ -309,6 +310,7 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
bus_speed = 400000;
}
+ iproc_i2c->bus_speed = bus_speed;
val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
@@ -439,6 +441,60 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+
+static int bcm_iproc_i2c_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);
+
+ /* make sure there's no pending interrupt when we go into suspend */
+ writel(0, iproc_i2c->base + IE_OFFSET);
+ readl(iproc_i2c->base + IE_OFFSET);
+ synchronize_irq(iproc_i2c->irq);
+
+ /* now disable the controller */
+ bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+
+ return 0;
+}
+
+static int bcm_iproc_i2c_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);
+ int ret;
+ u32 val;
+
+ /*
+ * Power domain could have been shut off completely in system deep
+ * sleep, so re-initialize the block here
+ */
+ ret = bcm_iproc_i2c_init(iproc_i2c);
+ if (ret)
+ return ret;
+
+ /* configure to the desired bus speed */
+ val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
+ val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
+ val |= (iproc_i2c->bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
+ writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
+
+ bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+
+ return 0;
+}
+
+static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = {
+ .suspend_late = &bcm_iproc_i2c_suspend,
+ .resume_early = &bcm_iproc_i2c_resume
+};
+
+#define BCM_IPROC_I2C_PM_OPS (&bcm_iproc_i2c_pm_ops)
+#else
+#define BCM_IPROC_I2C_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
static const struct of_device_id bcm_iproc_i2c_of_match[] = {
{ .compatible = "brcm,iproc-i2c" },
{ /* sentinel */ }
@@ -449,6 +505,7 @@ static struct platform_driver bcm_iproc_i2c_driver = {
.driver = {
.name = "bcm-iproc-i2c",
.of_match_table = bcm_iproc_i2c_of_match,
+ .pm = BCM_IPROC_I2C_PM_OPS,
},
.probe = bcm_iproc_i2c_probe,
.remove = bcm_iproc_i2c_remove,
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index c9336a3..3032b89 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -50,6 +50,11 @@
#define BCM2835_I2C_S_CLKT BIT(9)
#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */
+#define BCM2835_I2C_BITMSK_S 0x03FF
+
+#define BCM2835_I2C_CDIV_MIN 0x0002
+#define BCM2835_I2C_CDIV_MAX 0xFFFE
+
#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
struct bcm2835_i2c_dev {
@@ -111,6 +116,7 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
u32 val, err;
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ val &= BCM2835_I2C_BITMSK_S;
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
@@ -258,6 +264,11 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
*/
if (divider & 1)
divider++;
+ if ((divider < BCM2835_I2C_CDIV_MIN) ||
+ (divider > BCM2835_I2C_CDIV_MAX)) {
+ dev_err(&pdev->dev, "Invalid clock-frequency\n");
+ return -ENODEV;
+ }
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
new file mode 100644
index 0000000..8e9637e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#define N_DATA_REGS 8
+#define N_DATA_BYTES (N_DATA_REGS * 4)
+
+/* BSC count register field definitions */
+#define BSC_CNT_REG1_MASK 0x0000003f
+#define BSC_CNT_REG1_SHIFT 0
+#define BSC_CNT_REG2_MASK 0x00000fc0
+#define BSC_CNT_REG2_SHIFT 6
+
+/* BSC CTL register field definitions */
+#define BSC_CTL_REG_DTF_MASK 0x00000003
+#define BSC_CTL_REG_SCL_SEL_MASK 0x00000030
+#define BSC_CTL_REG_SCL_SEL_SHIFT 4
+#define BSC_CTL_REG_INT_EN_MASK 0x00000040
+#define BSC_CTL_REG_INT_EN_SHIFT 6
+#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080
+
+/* BSC_IIC_ENABLE r/w enable and interrupt field defintions */
+#define BSC_IIC_EN_RESTART_MASK 0x00000040
+#define BSC_IIC_EN_NOSTART_MASK 0x00000020
+#define BSC_IIC_EN_NOSTOP_MASK 0x00000010
+#define BSC_IIC_EN_NOACK_MASK 0x00000004
+#define BSC_IIC_EN_INTRP_MASK 0x00000002
+#define BSC_IIC_EN_ENABLE_MASK 0x00000001
+
+/* BSC_CTLHI control register field definitions */
+#define BSC_CTLHI_REG_INPUT_SWITCHING_LEVEL_MASK 0x00000080
+#define BSC_CTLHI_REG_DATAREG_SIZE_MASK 0x00000040
+#define BSC_CTLHI_REG_IGNORE_ACK_MASK 0x00000002
+#define BSC_CTLHI_REG_WAIT_DIS_MASK 0x00000001
+
+#define I2C_TIMEOUT 100 /* msecs */
+
+/* Condition mask used for non combined transfer */
+#define COND_RESTART BSC_IIC_EN_RESTART_MASK
+#define COND_NOSTART BSC_IIC_EN_NOSTART_MASK
+#define COND_NOSTOP BSC_IIC_EN_NOSTOP_MASK
+#define COND_START_STOP (COND_RESTART | COND_NOSTART | COND_NOSTOP)
+
+/* BSC data transfer direction */
+#define DTF_WR_MASK 0x00000000
+#define DTF_RD_MASK 0x00000001
+/* BSC data transfer direction combined format */
+#define DTF_RD_WR_MASK 0x00000002
+#define DTF_WR_RD_MASK 0x00000003
+
+#define INT_ENABLE true
+#define INT_DISABLE false
+
+/* BSC block register map structure to cache fields to be written */
+struct bsc_regs {
+ u32 chip_address; /* slave address */
+ u32 data_in[N_DATA_REGS]; /* tx data buffer*/
+ u32 cnt_reg; /* rx/tx data length */
+ u32 ctl_reg; /* control register */
+ u32 iic_enable; /* xfer enable and status */
+ u32 data_out[N_DATA_REGS]; /* rx data buffer */
+ u32 ctlhi_reg; /* more control fields */
+ u32 scl_param; /* reserved */
+};
+
+struct bsc_clk_param {
+ u32 hz;
+ u32 scl_mask;
+ u32 div_mask;
+};
+
+enum bsc_xfer_cmd {
+ CMD_WR,
+ CMD_RD,
+ CMD_WR_NOACK,
+ CMD_RD_NOACK,
+};
+
+static char const *cmd_string[] = {
+ [CMD_WR] = "WR",
+ [CMD_RD] = "RD",
+ [CMD_WR_NOACK] = "WR NOACK",
+ [CMD_RD_NOACK] = "RD NOACK",
+};
+
+enum bus_speeds {
+ SPD_375K,
+ SPD_390K,
+ SPD_187K,
+ SPD_200K,
+ SPD_93K,
+ SPD_97K,
+ SPD_46K,
+ SPD_50K
+};
+
+static const struct bsc_clk_param bsc_clk[] = {
+ [SPD_375K] = {
+ .hz = 375000,
+ .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = 0
+ },
+ [SPD_390K] = {
+ .hz = 390000,
+ .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = 0
+ },
+ [SPD_187K] = {
+ .hz = 187500,
+ .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = 0
+ },
+ [SPD_200K] = {
+ .hz = 200000,
+ .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = 0
+ },
+ [SPD_93K] = {
+ .hz = 93750,
+ .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = BSC_CTL_REG_DIV_CLK_MASK
+ },
+ [SPD_97K] = {
+ .hz = 97500,
+ .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = BSC_CTL_REG_DIV_CLK_MASK
+ },
+ [SPD_46K] = {
+ .hz = 46875,
+ .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = BSC_CTL_REG_DIV_CLK_MASK
+ },
+ [SPD_50K] = {
+ .hz = 50000,
+ .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT,
+ .div_mask = BSC_CTL_REG_DIV_CLK_MASK
+ }
+};
+
+struct brcmstb_i2c_dev {
+ struct device *device;
+ void __iomem *base;
+ void __iomem *irq_base;
+ int irq;
+ struct bsc_regs *bsc_regmap;
+ struct i2c_adapter adapter;
+ struct completion done;
+ bool is_suspended;
+ u32 clk_freq_hz;
+};
+
+/* register accessors for both be and le cpu arch */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define __bsc_readl(_reg) ioread32be(_reg)
+#define __bsc_writel(_val, _reg) iowrite32be(_val, _reg)
+#else
+#define __bsc_readl(_reg) ioread32(_reg)
+#define __bsc_writel(_val, _reg) iowrite32(_val, _reg)
+#endif
+
+#define bsc_readl(_dev, _reg) \
+ __bsc_readl(_dev->base + offsetof(struct bsc_regs, _reg))
+
+#define bsc_writel(_dev, _val, _reg) \
+ __bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg))
+
+static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev,
+ bool int_en)
+{
+
+ if (int_en)
+ /* Enable BSC CTL interrupt line */
+ dev->bsc_regmap->ctl_reg |= BSC_CTL_REG_INT_EN_MASK;
+ else
+ /* Disable BSC CTL interrupt line */
+ dev->bsc_regmap->ctl_reg &= ~BSC_CTL_REG_INT_EN_MASK;
+
+ barrier();
+ bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg);
+}
+
+static irqreturn_t brcmstb_i2c_isr(int irq, void *devid)
+{
+ struct brcmstb_i2c_dev *dev = devid;
+ u32 status_bsc_ctl = bsc_readl(dev, ctl_reg);
+ u32 status_iic_intrp = bsc_readl(dev, iic_enable);
+
+ dev_dbg(dev->device, "isr CTL_REG %x IIC_EN %x\n",
+ status_bsc_ctl, status_iic_intrp);
+
+ if (!(status_bsc_ctl & BSC_CTL_REG_INT_EN_MASK))
+ return IRQ_NONE;
+
+ brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
+ complete_all(&dev->done);
+
+ dev_dbg(dev->device, "isr handled");
+ return IRQ_HANDLED;
+}
+
+/* Wait for device to be ready */
+static int brcmstb_i2c_wait_if_busy(struct brcmstb_i2c_dev *dev)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT);
+
+ while ((bsc_readl(dev, iic_enable) & BSC_IIC_EN_INTRP_MASK)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+ return 0;
+}
+
+/* i2c xfer completion function, handles both irq and polling mode */
+static int brcmstb_i2c_wait_for_completion(struct brcmstb_i2c_dev *dev)
+{
+ int ret = 0;
+ unsigned long timeout = msecs_to_jiffies(I2C_TIMEOUT);
+
+ if (dev->irq >= 0) {
+ if (!wait_for_completion_timeout(&dev->done, timeout))
+ ret = -ETIMEDOUT;
+ } else {
+ /* we are in polling mode */
+ u32 bsc_intrp;
+ unsigned long time_left = jiffies + timeout;
+
+ do {
+ bsc_intrp = bsc_readl(dev, iic_enable) &
+ BSC_IIC_EN_INTRP_MASK;
+ if (time_after(jiffies, time_left)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ cpu_relax();
+ } while (!bsc_intrp);
+ }
+
+ if (dev->irq < 0 || ret == -ETIMEDOUT)
+ brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
+
+ return ret;
+}
+
+/* Set xfer START/STOP conditions for subsequent transfer */
+static void brcmstb_set_i2c_start_stop(struct brcmstb_i2c_dev *dev,
+ u32 cond_flag)
+{
+ u32 regval = dev->bsc_regmap->iic_enable;
+
+ dev->bsc_regmap->iic_enable = (regval & ~COND_START_STOP) | cond_flag;
+}
+
+/* Send I2C request check completion */
+static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev,
+ enum bsc_xfer_cmd cmd)
+{
+ int rc = 0;
+ struct bsc_regs *pi2creg = dev->bsc_regmap;
+
+ /* Make sure the hardware is ready */
+ rc = brcmstb_i2c_wait_if_busy(dev);
+ if (rc < 0)
+ return rc;
+
+ /* only if we are in interrupt mode */
+ if (dev->irq >= 0)
+ reinit_completion(&dev->done);
+
+ /* enable BSC CTL interrupt line */
+ brcmstb_i2c_enable_disable_irq(dev, INT_ENABLE);
+
+ /* initiate transfer by setting iic_enable */
+ pi2creg->iic_enable |= BSC_IIC_EN_ENABLE_MASK;
+ bsc_writel(dev, pi2creg->iic_enable, iic_enable);
+
+ /* Wait for transaction to finish or timeout */
+ rc = brcmstb_i2c_wait_for_completion(dev);
+ if (rc) {
+ dev_dbg(dev->device, "intr timeout for cmd %s\n",
+ cmd_string[cmd]);
+ goto cmd_out;
+ }
+
+ if ((CMD_RD || CMD_WR) &&
+ bsc_readl(dev, iic_enable) & BSC_IIC_EN_NOACK_MASK) {
+ rc = -EREMOTEIO;
+ dev_dbg(dev->device, "controller received NOACK intr for %s\n",
+ cmd_string[cmd]);
+ }
+
+cmd_out:
+ bsc_writel(dev, 0, cnt_reg);
+ bsc_writel(dev, 0, iic_enable);
+
+ return rc;
+}
+
+/* Actual data transfer through the BSC master */
+static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
+ u8 *buf, unsigned int len,
+ struct i2c_msg *pmsg)
+{
+ int cnt, byte, rc;
+ enum bsc_xfer_cmd cmd;
+ u32 ctl_reg;
+ struct bsc_regs *pi2creg = dev->bsc_regmap;
+ int no_ack = pmsg->flags & I2C_M_IGNORE_NAK;
+
+ /* see if the transaction needs to check NACK conditions */
+ if (no_ack || len <= N_DATA_BYTES) {
+ cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK
+ : CMD_WR_NOACK;
+ pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK;
+ } else {
+ cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD : CMD_WR;
+ pi2creg->ctlhi_reg &= ~BSC_CTLHI_REG_IGNORE_ACK_MASK;
+ }
+ bsc_writel(dev, pi2creg->ctlhi_reg, ctlhi_reg);
+
+ /* set data transfer direction */
+ ctl_reg = pi2creg->ctl_reg & ~BSC_CTL_REG_DTF_MASK;
+ if (cmd == CMD_WR || cmd == CMD_WR_NOACK)
+ pi2creg->ctl_reg = ctl_reg | DTF_WR_MASK;
+ else
+ pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK;
+
+ /* set the read/write length */
+ bsc_writel(dev, BSC_CNT_REG1_MASK & (len << BSC_CNT_REG1_SHIFT),
+ cnt_reg);
+
+ /* Write data into data_in register */
+ if (cmd == CMD_WR || cmd == CMD_WR_NOACK) {
+ for (cnt = 0; cnt < len; cnt += 4) {
+ u32 word = 0;
+
+ for (byte = 0; byte < 4; byte++) {
+ word >>= 8;
+ if ((cnt + byte) < len)
+ word |= buf[cnt + byte] << 24;
+ }
+ bsc_writel(dev, word, data_in[cnt >> 2]);
+ }
+ }
+
+ /* Initiate xfer, the function will return on completion */
+ rc = brcmstb_send_i2c_cmd(dev, cmd);
+
+ if (rc != 0) {
+ dev_dbg(dev->device, "%s failure", cmd_string[cmd]);
+ return rc;
+ }
+
+ if (cmd == CMD_RD || cmd == CMD_RD_NOACK) {
+ for (cnt = 0; cnt < len; cnt += 4) {
+ u32 data = bsc_readl(dev, data_out[cnt >> 2]);
+
+ for (byte = 0; byte < 4 &&
+ (byte + cnt) < len; byte++) {
+ buf[cnt + byte] = data & 0xff;
+ data >>= 8;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Write a single byte of data to the i2c bus */
+static int brcmstb_i2c_write_data_byte(struct brcmstb_i2c_dev *dev,
+ u8 *buf, unsigned int nak_expected)
+{
+ enum bsc_xfer_cmd cmd = nak_expected ? CMD_WR : CMD_WR_NOACK;
+
+ bsc_writel(dev, 1, cnt_reg);
+ bsc_writel(dev, *buf, data_in);
+
+ return brcmstb_send_i2c_cmd(dev, cmd);
+}
+
+/* Send i2c address */
+static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
+ struct i2c_msg *msg)
+{
+ unsigned char addr;
+
+ if (msg->flags & I2C_M_TEN) {
+ /* First byte is 11110XX0 where XX is upper 2 bits */
+ addr = 0xF0 | ((msg->addr & 0x300) >> 7);
+ bsc_writel(dev, addr, chip_address);
+
+ /* Second byte is the remaining 8 bits */
+ addr = msg->addr & 0xFF;
+ if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
+ return -EREMOTEIO;
+
+ if (msg->flags & I2C_M_RD) {
+ /* For read, send restart without stop condition */
+ brcmstb_set_i2c_start_stop(dev, COND_RESTART
+ | COND_NOSTOP);
+ /* Then re-send the first byte with the read bit set */
+ addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01;
+ if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0)
+ return -EREMOTEIO;
+
+ }
+ } else {
+ addr = msg->addr << 1;
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+
+ bsc_writel(dev, addr, chip_address);
+ }
+
+ return 0;
+}
+
+/* Master transfer function */
+static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msgs[], int num)
+{
+ struct brcmstb_i2c_dev *dev = i2c_get_adapdata(adapter);
+ struct i2c_msg *pmsg;
+ int rc = 0;
+ int i;
+ int bytes_to_xfer;
+ u8 *tmp_buf;
+ int len = 0;
+
+ if (dev->is_suspended)
+ return -EBUSY;
+
+ /* Loop through all messages */
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ len = pmsg->len;
+ tmp_buf = pmsg->buf;
+
+ dev_dbg(dev->device,
+ "msg# %d/%d flg %x buf %x len %d\n", i,
+ num - 1, pmsg->flags,
+ pmsg->buf ? pmsg->buf[0] : '0', pmsg->len);
+
+ if (i < (num - 1) && (msgs[i + 1].flags & I2C_M_NOSTART))
+ brcmstb_set_i2c_start_stop(dev, ~(COND_START_STOP));
+ else
+ brcmstb_set_i2c_start_stop(dev,
+ COND_RESTART | COND_NOSTOP);
+
+ /* Send slave address */
+ if (!(pmsg->flags & I2C_M_NOSTART)) {
+ rc = brcmstb_i2c_do_addr(dev, pmsg);
+ if (rc < 0) {
+ dev_dbg(dev->device,
+ "NACK for addr %2.2x msg#%d rc = %d\n",
+ pmsg->addr, i, rc);
+ goto out;
+ }
+ }
+
+ /* Perform data transfer */
+ while (len) {
+ bytes_to_xfer = min(len, N_DATA_BYTES);
+
+ if (len <= N_DATA_BYTES && i == (num - 1))
+ brcmstb_set_i2c_start_stop(dev,
+ ~(COND_START_STOP));
+
+ rc = brcmstb_i2c_xfer_bsc_data(dev, tmp_buf,
+ bytes_to_xfer, pmsg);
+ if (rc < 0)
+ goto out;
+
+ len -= bytes_to_xfer;
+ tmp_buf += bytes_to_xfer;
+ }
+ }
+
+ rc = num;
+out:
+ return rc;
+
+}
+
+static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR
+ | I2C_FUNC_NOSTART | I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+static const struct i2c_algorithm brcmstb_i2c_algo = {
+ .master_xfer = brcmstb_i2c_xfer,
+ .functionality = brcmstb_i2c_functionality,
+};
+
+static void brcmstb_i2c_set_bus_speed(struct brcmstb_i2c_dev *dev)
+{
+ int i = 0, num_speeds = ARRAY_SIZE(bsc_clk);
+ u32 clk_freq_hz = dev->clk_freq_hz;
+
+ for (i = 0; i < num_speeds; i++) {
+ if (bsc_clk[i].hz == clk_freq_hz) {
+ dev->bsc_regmap->ctl_reg &= ~(BSC_CTL_REG_SCL_SEL_MASK
+ | BSC_CTL_REG_DIV_CLK_MASK);
+ dev->bsc_regmap->ctl_reg |= (bsc_clk[i].scl_mask |
+ bsc_clk[i].div_mask);
+ bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg);
+ break;
+ }
+ }
+
+ /* in case we did not get find a valid speed */
+ if (i == num_speeds) {
+ i = (bsc_readl(dev, ctl_reg) & BSC_CTL_REG_SCL_SEL_MASK) >>
+ BSC_CTL_REG_SCL_SEL_SHIFT;
+ dev_warn(dev->device, "leaving current clock-frequency @ %dHz\n",
+ bsc_clk[i].hz);
+ }
+}
+
+static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
+{
+ /* 4 byte data register */
+ dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK;
+ bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg);
+ /* set bus speed */
+ brcmstb_i2c_set_bus_speed(dev);
+}
+
+static int brcmstb_i2c_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct brcmstb_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *iomem;
+ const char *int_name;
+
+ /* Allocate memory for private data structure */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->bsc_regmap = devm_kzalloc(&pdev->dev, sizeof(struct bsc_regs *),
+ GFP_KERNEL);
+ if (!dev->bsc_regmap)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dev);
+ dev->device = &pdev->dev;
+ init_completion(&dev->done);
+
+ /* Map hardware registers */
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->base = devm_ioremap_resource(dev->device, iomem);
+ if (IS_ERR(dev->base)) {
+ rc = -ENOMEM;
+ goto probe_errorout;
+ }
+
+ rc = of_property_read_string(dev->device->of_node, "interrupt-names",
+ &int_name);
+ if (rc < 0)
+ int_name = NULL;
+
+ /* Get the interrupt number */
+ dev->irq = platform_get_irq(pdev, 0);
+
+ /* disable the bsc interrupt line */
+ brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
+
+ /* register the ISR handler */
+ rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
+ IRQF_SHARED,
+ int_name ? int_name : pdev->name,
+ dev);
+
+ if (rc) {
+ dev_dbg(dev->device, "falling back to polling mode");
+ dev->irq = -1;
+ }
+
+ if (of_property_read_u32(dev->device->of_node,
+ "clock-frequency", &dev->clk_freq_hz)) {
+ dev_warn(dev->device, "setting clock-frequency@%dHz\n",
+ bsc_clk[0].hz);
+ dev->clk_freq_hz = bsc_clk[0].hz;
+ }
+
+ brcmstb_i2c_set_bsc_reg_defaults(dev);
+
+ /* Add the i2c adapter */
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ strlcpy(adap->name, "Broadcom STB : ", sizeof(adap->name));
+ if (int_name)
+ strlcat(adap->name, int_name, sizeof(adap->name));
+ adap->algo = &brcmstb_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
+ rc = i2c_add_adapter(adap);
+ if (rc) {
+ dev_err(dev->device, "failed to add adapter\n");
+ goto probe_errorout;
+ }
+
+ dev_info(dev->device, "%s@%dhz registered in %s mode\n",
+ int_name ? int_name : " ", dev->clk_freq_hz,
+ (dev->irq >= 0) ? "interrupt" : "polling");
+
+ return 0;
+
+probe_errorout:
+ return rc;
+}
+
+static int brcmstb_i2c_remove(struct platform_device *pdev)
+{
+ struct brcmstb_i2c_dev *dev = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dev->adapter);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmstb_i2c_suspend(struct device *dev)
+{
+ struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+
+ i2c_lock_adapter(&i2c_dev->adapter);
+ i2c_dev->is_suspended = true;
+ i2c_unlock_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+
+static int brcmstb_i2c_resume(struct device *dev)
+{
+ struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
+
+ i2c_lock_adapter(&i2c_dev->adapter);
+ brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
+ i2c_dev->is_suspended = false;
+ i2c_unlock_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
+ brcmstb_i2c_resume);
+
+static const struct of_device_id brcmstb_i2c_of_match[] = {
+ {.compatible = "brcm,brcmstb-i2c"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
+
+static struct platform_driver brcmstb_i2c_driver = {
+ .driver = {
+ .name = "brcmstb-i2c",
+ .of_match_table = brcmstb_i2c_of_match,
+ .pm = &brcmstb_i2c_pm,
+ },
+ .probe = brcmstb_i2c_probe,
+ .remove = brcmstb_i2c_remove,
+};
+module_platform_driver(brcmstb_i2c_driver);
+
+MODULE_AUTHOR("Kamal Dasu <kdasu@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Settop I2C Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 4788a32..3fbb9a0 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -41,8 +41,8 @@
#define DAVINCI_I2C_TIMEOUT (1*HZ)
#define DAVINCI_I2C_MAX_TRIES 2
-#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \
- DAVINCI_I2C_IMR_SCD | \
+#define DAVINCI_I2C_OWN_ADDRESS 0x08
+#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_SCD | \
DAVINCI_I2C_IMR_ARDY | \
DAVINCI_I2C_IMR_NACK | \
DAVINCI_I2C_IMR_AL)
@@ -204,9 +204,30 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
psc++; /* better to run under spec than over */
d = (psc >= 2) ? 5 : 7 - psc;
- clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)) - (d << 1);
- clkh = clk >> 1;
- clkl = clk - clkh;
+ clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000));
+ /* Avoid driving the bus too fast because of rounding errors above */
+ if (input_clock / (psc + 1) / clk > pdata->bus_freq * 1000)
+ clk++;
+ /*
+ * According to I2C-BUS Spec 2.1, in FAST-MODE LOW period should be at
+ * least 1.3uS, which is not the case with 50% duty cycle. Driving HIGH
+ * to LOW ratio as 1 to 2 is more safe.
+ */
+ if (pdata->bus_freq > 100)
+ clkl = (clk << 1) / 3;
+ else
+ clkl = (clk >> 1);
+ /*
+ * It's not always possible to have 1 to 2 ratio when d=7, so fall back
+ * to minimal possible clkh in this case.
+ */
+ if (clk >= clkl + d) {
+ clkh = clk - clkl - d;
+ clkl -= d;
+ } else {
+ clkh = 0;
+ clkl = clk - (d << 1);
+ }
davinci_i2c_write_reg(dev, DAVINCI_I2C_PSC_REG, psc);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh);
@@ -233,7 +254,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
/* Respond at reserved "SMBus Host" slave address" (and zero);
* we seem to have no option to not respond...
*/
- davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08);
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, DAVINCI_I2C_OWN_ADDRESS);
dev_dbg(dev->dev, "PSC = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG));
@@ -350,29 +371,25 @@ static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = {
/*
* Waiting for bus not busy
*/
-static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev,
- char allow_sleep)
+static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev)
{
- unsigned long timeout;
- static u16 to_cnt;
-
- timeout = jiffies + dev->adapter.timeout;
- while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG)
- & DAVINCI_I2C_STR_BB) {
- if (to_cnt <= DAVINCI_I2C_MAX_TRIES) {
- if (time_after(jiffies, timeout)) {
- dev_warn(dev->dev,
- "timeout waiting for bus ready\n");
- to_cnt++;
- return -ETIMEDOUT;
- } else {
- to_cnt = 0;
- i2c_recover_bus(&dev->adapter);
- }
- }
- if (allow_sleep)
- schedule_timeout(1);
- }
+ unsigned long timeout = jiffies + dev->adapter.timeout;
+
+ do {
+ if (!(davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB))
+ return 0;
+ schedule_timeout_uninterruptible(1);
+ } while (time_before_eq(jiffies, timeout));
+
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
+ i2c_recover_bus(&dev->adapter);
+
+ /*
+ * if bus is still "busy" here, it's most probably a HW problem like
+ * short-circuit
+ */
+ if (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB)
+ return -EIO;
return 0;
}
@@ -390,6 +407,11 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
u16 w;
unsigned long time_left;
+ if (msg->addr == DAVINCI_I2C_OWN_ADDRESS) {
+ dev_warn(dev->dev, "transfer to own address aborted\n");
+ return -EADDRNOTAVAIL;
+ }
+
/* Introduce a delay, required for some boards (e.g Davinci EVM) */
if (pdata->bus_delay)
udelay(pdata->bus_delay);
@@ -505,7 +527,7 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
- ret = i2c_davinci_wait_bus_not_busy(dev, 1);
+ ret = i2c_davinci_wait_bus_not_busy(dev);
if (ret < 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return ret;
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 0a80e4a..3dd2de3 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -281,7 +281,8 @@ static int dw_i2c_remove(struct platform_device *pdev)
i2c_dw_disable(dev);
- pm_runtime_put(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (has_acpi_companion(&pdev->dev))
@@ -298,6 +299,22 @@ static const struct of_device_id dw_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
#endif
+#ifdef CONFIG_PM_SLEEP
+static int dw_i2c_prepare(struct device *dev)
+{
+ return pm_runtime_suspended(dev);
+}
+
+static void dw_i2c_complete(struct device *dev)
+{
+ if (dev->power.direct_complete)
+ pm_request_resume(dev);
+}
+#else
+#define dw_i2c_prepare NULL
+#define dw_i2c_complete NULL
+#endif
+
#ifdef CONFIG_PM
static int dw_i2c_suspend(struct device *dev)
{
@@ -322,10 +339,18 @@ static int dw_i2c_resume(struct device *dev)
return 0;
}
-#endif
-static UNIVERSAL_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend,
- dw_i2c_resume, NULL);
+static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
+ .prepare = dw_i2c_prepare,
+ .complete = dw_i2c_complete,
+ SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_suspend, dw_i2c_resume)
+ SET_RUNTIME_PM_OPS(dw_i2c_suspend, dw_i2c_resume, NULL)
+};
+
+#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
+#else
+#define DW_I2C_DEV_PMOPS NULL
+#endif
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:i2c_designware");
@@ -337,7 +362,7 @@ static struct platform_driver dw_i2c_driver = {
.name = "i2c_designware",
.of_match_table = of_match_ptr(dw_i2c_of_match),
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),
- .pm = &dw_i2c_dev_pm_ops,
+ .pm = DW_I2C_DEV_PMOPS,
},
};
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index a53a7dd..785aa67 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -241,7 +241,7 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = {
};
-static struct platform_device_id imx_i2c_devtype[] = {
+static const struct platform_device_id imx_i2c_devtype[] = {
{
.name = "imx1-i2c",
.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
new file mode 100644
index 0000000..9920eef
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Xudong Chen <xudong.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#define I2C_RS_TRANSFER (1 << 4)
+#define I2C_HS_NACKERR (1 << 2)
+#define I2C_ACKERR (1 << 1)
+#define I2C_TRANSAC_COMP (1 << 0)
+#define I2C_TRANSAC_START (1 << 0)
+#define I2C_RS_MUL_CNFG (1 << 15)
+#define I2C_RS_MUL_TRIG (1 << 14)
+#define I2C_DCM_DISABLE 0x0000
+#define I2C_IO_CONFIG_OPEN_DRAIN 0x0003
+#define I2C_IO_CONFIG_PUSH_PULL 0x0000
+#define I2C_SOFT_RST 0x0001
+#define I2C_FIFO_ADDR_CLR 0x0001
+#define I2C_DELAY_LEN 0x0002
+#define I2C_ST_START_CON 0x8001
+#define I2C_FS_START_CON 0x1800
+#define I2C_TIME_CLR_VALUE 0x0000
+#define I2C_TIME_DEFAULT_VALUE 0x0003
+#define I2C_FS_TIME_INIT_VALUE 0x1303
+#define I2C_WRRD_TRANAC_VALUE 0x0002
+#define I2C_RD_TRANAC_VALUE 0x0001
+
+#define I2C_DMA_CON_TX 0x0000
+#define I2C_DMA_CON_RX 0x0001
+#define I2C_DMA_START_EN 0x0001
+#define I2C_DMA_INT_FLAG_NONE 0x0000
+#define I2C_DMA_CLR_FLAG 0x0000
+
+#define I2C_DEFAULT_SPEED 100000 /* hz */
+#define MAX_FS_MODE_SPEED 400000
+#define MAX_HS_MODE_SPEED 3400000
+#define MAX_SAMPLE_CNT_DIV 8
+#define MAX_STEP_CNT_DIV 64
+#define MAX_HS_STEP_CNT_DIV 8
+
+#define I2C_CONTROL_RS (0x1 << 1)
+#define I2C_CONTROL_DMA_EN (0x1 << 2)
+#define I2C_CONTROL_CLK_EXT_EN (0x1 << 3)
+#define I2C_CONTROL_DIR_CHANGE (0x1 << 4)
+#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5)
+#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6)
+#define I2C_CONTROL_WRAPPER (0x1 << 0)
+
+#define I2C_DRV_NAME "i2c-mt65xx"
+
+enum DMA_REGS_OFFSET {
+ OFFSET_INT_FLAG = 0x0,
+ OFFSET_INT_EN = 0x04,
+ OFFSET_EN = 0x08,
+ OFFSET_CON = 0x18,
+ OFFSET_TX_MEM_ADDR = 0x1c,
+ OFFSET_RX_MEM_ADDR = 0x20,
+ OFFSET_TX_LEN = 0x24,
+ OFFSET_RX_LEN = 0x28,
+};
+
+enum i2c_trans_st_rs {
+ I2C_TRANS_STOP = 0,
+ I2C_TRANS_REPEATED_START,
+};
+
+enum mtk_trans_op {
+ I2C_MASTER_WR = 1,
+ I2C_MASTER_RD,
+ I2C_MASTER_WRRD,
+};
+
+enum I2C_REGS_OFFSET {
+ OFFSET_DATA_PORT = 0x0,
+ OFFSET_SLAVE_ADDR = 0x04,
+ OFFSET_INTR_MASK = 0x08,
+ OFFSET_INTR_STAT = 0x0c,
+ OFFSET_CONTROL = 0x10,
+ OFFSET_TRANSFER_LEN = 0x14,
+ OFFSET_TRANSAC_LEN = 0x18,
+ OFFSET_DELAY_LEN = 0x1c,
+ OFFSET_TIMING = 0x20,
+ OFFSET_START = 0x24,
+ OFFSET_EXT_CONF = 0x28,
+ OFFSET_FIFO_STAT = 0x30,
+ OFFSET_FIFO_THRESH = 0x34,
+ OFFSET_FIFO_ADDR_CLR = 0x38,
+ OFFSET_IO_CONFIG = 0x40,
+ OFFSET_RSV_DEBUG = 0x44,
+ OFFSET_HS = 0x48,
+ OFFSET_SOFTRESET = 0x50,
+ OFFSET_DCM_EN = 0x54,
+ OFFSET_PATH_DIR = 0x60,
+ OFFSET_DEBUGSTAT = 0x64,
+ OFFSET_DEBUGCTRL = 0x68,
+ OFFSET_TRANSFER_LEN_AUX = 0x6c,
+};
+
+struct mtk_i2c_compatible {
+ const struct i2c_adapter_quirks *quirks;
+ unsigned char pmic_i2c: 1;
+ unsigned char dcm: 1;
+ unsigned char auto_restart: 1;
+};
+
+struct mtk_i2c {
+ struct i2c_adapter adap; /* i2c host adapter */
+ struct device *dev;
+ struct completion msg_complete;
+
+ /* set in i2c probe */
+ void __iomem *base; /* i2c base addr */
+ void __iomem *pdmabase; /* dma base address*/
+ struct clk *clk_main; /* main clock for i2c bus */
+ struct clk *clk_dma; /* DMA clock for i2c via DMA */
+ struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
+ bool have_pmic; /* can use i2c pins from PMIC */
+ bool use_push_pull; /* IO config push-pull mode */
+
+ u16 irq_stat; /* interrupt status */
+ unsigned int speed_hz; /* The speed in transfer */
+ enum mtk_trans_op op;
+ u16 timing_reg;
+ u16 high_speed_reg;
+ const struct mtk_i2c_compatible *dev_comp;
+};
+
+static const struct i2c_adapter_quirks mt6577_i2c_quirks = {
+ .flags = I2C_AQ_COMB_WRITE_THEN_READ,
+ .max_num_msgs = 1,
+ .max_write_len = 255,
+ .max_read_len = 255,
+ .max_comb_1st_msg_len = 255,
+ .max_comb_2nd_msg_len = 31,
+};
+
+static const struct i2c_adapter_quirks mt8173_i2c_quirks = {
+ .max_num_msgs = 65535,
+ .max_write_len = 65535,
+ .max_read_len = 65535,
+ .max_comb_1st_msg_len = 65535,
+ .max_comb_2nd_msg_len = 65535,
+};
+
+static const struct mtk_i2c_compatible mt6577_compat = {
+ .quirks = &mt6577_i2c_quirks,
+ .pmic_i2c = 0,
+ .dcm = 1,
+ .auto_restart = 0,
+};
+
+static const struct mtk_i2c_compatible mt6589_compat = {
+ .quirks = &mt6577_i2c_quirks,
+ .pmic_i2c = 1,
+ .dcm = 0,
+ .auto_restart = 0,
+};
+
+static const struct mtk_i2c_compatible mt8173_compat = {
+ .quirks = &mt8173_i2c_quirks,
+ .pmic_i2c = 0,
+ .dcm = 1,
+ .auto_restart = 1,
+};
+
+static const struct of_device_id mtk_i2c_of_match[] = {
+ { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat },
+ { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
+ { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
+
+static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
+{
+ int ret;
+
+ ret = clk_prepare_enable(i2c->clk_dma);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(i2c->clk_main);
+ if (ret)
+ goto err_main;
+
+ if (i2c->have_pmic) {
+ ret = clk_prepare_enable(i2c->clk_pmic);
+ if (ret)
+ goto err_pmic;
+ }
+ return 0;
+
+err_pmic:
+ clk_disable_unprepare(i2c->clk_main);
+err_main:
+ clk_disable_unprepare(i2c->clk_dma);
+
+ return ret;
+}
+
+static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
+{
+ if (i2c->have_pmic)
+ clk_disable_unprepare(i2c->clk_pmic);
+
+ clk_disable_unprepare(i2c->clk_main);
+ clk_disable_unprepare(i2c->clk_dma);
+}
+
+static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
+{
+ u16 control_reg;
+
+ writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET);
+
+ /* Set ioconfig */
+ if (i2c->use_push_pull)
+ writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG);
+ else
+ writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG);
+
+ if (i2c->dev_comp->dcm)
+ writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN);
+
+ writew(i2c->timing_reg, i2c->base + OFFSET_TIMING);
+ writew(i2c->high_speed_reg, i2c->base + OFFSET_HS);
+
+ /* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */
+ if (i2c->have_pmic)
+ writew(I2C_CONTROL_WRAPPER, i2c->base + OFFSET_PATH_DIR);
+
+ control_reg = I2C_CONTROL_ACKERR_DET_EN |
+ I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
+ writew(control_reg, i2c->base + OFFSET_CONTROL);
+ writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN);
+}
+
+/*
+ * Calculate i2c port speed
+ *
+ * Hardware design:
+ * i2c_bus_freq = parent_clk / (clock_div * 2 * sample_cnt * step_cnt)
+ * clock_div: fixed in hardware, but may be various in different SoCs
+ *
+ * The calculation want to pick the highest bus frequency that is still
+ * less than or equal to i2c->speed_hz. The calculation try to get
+ * sample_cnt and step_cn
+ */
+static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk,
+ unsigned int clock_div)
+{
+ unsigned int clk_src;
+ unsigned int step_cnt;
+ unsigned int sample_cnt;
+ unsigned int max_step_cnt;
+ unsigned int target_speed;
+ unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV;
+ unsigned int base_step_cnt;
+ unsigned int opt_div;
+ unsigned int best_mul;
+ unsigned int cnt_mul;
+
+ clk_src = parent_clk / clock_div;
+ target_speed = i2c->speed_hz;
+
+ if (target_speed > MAX_HS_MODE_SPEED)
+ target_speed = MAX_HS_MODE_SPEED;
+
+ if (target_speed > MAX_FS_MODE_SPEED)
+ max_step_cnt = MAX_HS_STEP_CNT_DIV;
+ else
+ max_step_cnt = MAX_STEP_CNT_DIV;
+
+ base_step_cnt = max_step_cnt;
+ /* Find the best combination */
+ opt_div = DIV_ROUND_UP(clk_src >> 1, target_speed);
+ best_mul = MAX_SAMPLE_CNT_DIV * max_step_cnt;
+
+ /* Search for the best pair (sample_cnt, step_cnt) with
+ * 0 < sample_cnt < MAX_SAMPLE_CNT_DIV
+ * 0 < step_cnt < max_step_cnt
+ * sample_cnt * step_cnt >= opt_div
+ * optimizing for sample_cnt * step_cnt being minimal
+ */
+ for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
+ step_cnt = DIV_ROUND_UP(opt_div, sample_cnt);
+ cnt_mul = step_cnt * sample_cnt;
+ if (step_cnt > max_step_cnt)
+ continue;
+
+ if (cnt_mul < best_mul) {
+ best_mul = cnt_mul;
+ base_sample_cnt = sample_cnt;
+ base_step_cnt = step_cnt;
+ if (best_mul == opt_div)
+ break;
+ }
+ }
+
+ sample_cnt = base_sample_cnt;
+ step_cnt = base_step_cnt;
+
+ if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) {
+ /* In this case, hardware can't support such
+ * low i2c_bus_freq
+ */
+ dev_dbg(i2c->dev, "Unsupported speed (%uhz)\n", target_speed);
+ return -EINVAL;
+ }
+
+ step_cnt--;
+ sample_cnt--;
+
+ if (target_speed > MAX_FS_MODE_SPEED) {
+ /* Set the high speed mode register */
+ i2c->timing_reg = I2C_FS_TIME_INIT_VALUE;
+ i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
+ (sample_cnt << 12) | (step_cnt << 8);
+ } else {
+ i2c->timing_reg = (sample_cnt << 8) | (step_cnt << 0);
+ /* Disable the high speed transaction */
+ i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
+ }
+
+ return 0;
+}
+
+static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
+ int num, int left_num)
+{
+ u16 addr_reg;
+ u16 start_reg;
+ u16 control_reg;
+ u16 restart_flag = 0;
+ dma_addr_t rpaddr = 0;
+ dma_addr_t wpaddr = 0;
+ int ret;
+
+ i2c->irq_stat = 0;
+
+ if (i2c->dev_comp->auto_restart)
+ restart_flag = I2C_RS_TRANSFER;
+
+ reinit_completion(&i2c->msg_complete);
+
+ control_reg = readw(i2c->base + OFFSET_CONTROL) &
+ ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
+ if ((i2c->speed_hz > 400000) || (left_num >= 1))
+ control_reg |= I2C_CONTROL_RS;
+
+ if (i2c->op == I2C_MASTER_WRRD)
+ control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
+
+ writew(control_reg, i2c->base + OFFSET_CONTROL);
+
+ /* set start condition */
+ if (i2c->speed_hz <= 100000)
+ writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
+ else
+ writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
+
+ addr_reg = msgs->addr << 1;
+ if (i2c->op == I2C_MASTER_RD)
+ addr_reg |= 0x1;
+
+ writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
+
+ /* Clear interrupt status */
+ writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT);
+ writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR);
+
+ /* Enable interrupt */
+ writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK);
+
+ /* Set transfer and transaction len */
+ if (i2c->op == I2C_MASTER_WRRD) {
+ writew(msgs->len | ((msgs + 1)->len) << 8,
+ i2c->base + OFFSET_TRANSFER_LEN);
+ writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
+ } else {
+ writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
+ writew(num, i2c->base + OFFSET_TRANSAC_LEN);
+ }
+
+ /* Prepare buffer data to start transfer */
+ if (i2c->op == I2C_MASTER_RD) {
+ writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
+ writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON);
+ rpaddr = dma_map_single(i2c->dev, msgs->buf,
+ msgs->len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(i2c->dev, rpaddr))
+ return -ENOMEM;
+ writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
+ writel(msgs->len, i2c->pdmabase + OFFSET_RX_LEN);
+ } else if (i2c->op == I2C_MASTER_WR) {
+ writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
+ writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
+ wpaddr = dma_map_single(i2c->dev, msgs->buf,
+ msgs->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(i2c->dev, wpaddr))
+ return -ENOMEM;
+ writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
+ writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
+ } else {
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
+ writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
+ wpaddr = dma_map_single(i2c->dev, msgs->buf,
+ msgs->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(i2c->dev, wpaddr))
+ return -ENOMEM;
+ rpaddr = dma_map_single(i2c->dev, (msgs + 1)->buf,
+ (msgs + 1)->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(i2c->dev, rpaddr)) {
+ dma_unmap_single(i2c->dev, wpaddr,
+ msgs->len, DMA_TO_DEVICE);
+ return -ENOMEM;
+ }
+ writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
+ writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
+ writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
+ writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN);
+ }
+
+ writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
+
+ if (!i2c->dev_comp->auto_restart) {
+ start_reg = I2C_TRANSAC_START;
+ } else {
+ start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
+ if (left_num >= 1)
+ start_reg |= I2C_RS_MUL_CNFG;
+ }
+ writew(start_reg, i2c->base + OFFSET_START);
+
+ ret = wait_for_completion_timeout(&i2c->msg_complete,
+ i2c->adap.timeout);
+
+ /* Clear interrupt mask */
+ writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+ I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
+
+ if (i2c->op == I2C_MASTER_WR) {
+ dma_unmap_single(i2c->dev, wpaddr,
+ msgs->len, DMA_TO_DEVICE);
+ } else if (i2c->op == I2C_MASTER_RD) {
+ dma_unmap_single(i2c->dev, rpaddr,
+ msgs->len, DMA_FROM_DEVICE);
+ } else {
+ dma_unmap_single(i2c->dev, wpaddr, msgs->len,
+ DMA_TO_DEVICE);
+ dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len,
+ DMA_FROM_DEVICE);
+ }
+
+ if (ret == 0) {
+ dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr);
+ mtk_i2c_init_hw(i2c);
+ return -ETIMEDOUT;
+ }
+
+ completion_done(&i2c->msg_complete);
+
+ if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
+ dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
+ mtk_i2c_init_hw(i2c);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int mtk_i2c_transfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ int ret;
+ int left_num = num;
+ struct mtk_i2c *i2c = i2c_get_adapdata(adap);
+
+ ret = mtk_i2c_clock_enable(i2c);
+ if (ret)
+ return ret;
+
+ while (left_num--) {
+ if (!msgs->buf) {
+ dev_dbg(i2c->dev, "data buffer is NULL.\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+
+ if (msgs->flags & I2C_M_RD)
+ i2c->op = I2C_MASTER_RD;
+ else
+ i2c->op = I2C_MASTER_WR;
+
+ if (!i2c->dev_comp->auto_restart) {
+ if (num > 1) {
+ /* combined two messages into one transaction */
+ i2c->op = I2C_MASTER_WRRD;
+ left_num--;
+ }
+ }
+
+ /* always use DMA mode. */
+ ret = mtk_i2c_do_transfer(i2c, msgs, num, left_num);
+ if (ret < 0)
+ goto err_exit;
+
+ msgs++;
+ }
+ /* the return value is number of executed messages */
+ ret = num;
+
+err_exit:
+ mtk_i2c_clock_disable(i2c);
+ return ret;
+}
+
+static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
+{
+ struct mtk_i2c *i2c = dev_id;
+ u16 restart_flag = 0;
+
+ if (i2c->dev_comp->auto_restart)
+ restart_flag = I2C_RS_TRANSFER;
+
+ i2c->irq_stat = readw(i2c->base + OFFSET_INTR_STAT);
+ writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR
+ | I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT);
+
+ complete(&i2c->msg_complete);
+
+ return IRQ_HANDLED;
+}
+
+static u32 mtk_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mtk_i2c_algorithm = {
+ .master_xfer = mtk_i2c_transfer,
+ .functionality = mtk_i2c_functionality,
+};
+
+static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c,
+ unsigned int *clk_src_div)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "clock-frequency", &i2c->speed_hz);
+ if (ret < 0)
+ i2c->speed_hz = I2C_DEFAULT_SPEED;
+
+ ret = of_property_read_u32(np, "clock-div", clk_src_div);
+ if (ret < 0)
+ return ret;
+
+ if (*clk_src_div == 0)
+ return -EINVAL;
+
+ i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic");
+ i2c->use_push_pull =
+ of_property_read_bool(np, "mediatek,use-push-pull");
+
+ return 0;
+}
+
+static int mtk_i2c_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id;
+ int ret = 0;
+ struct mtk_i2c *i2c;
+ struct clk *clk;
+ unsigned int clk_src_div;
+ struct resource *res;
+ int irq;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div);
+ if (ret)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->pdmabase))
+ return PTR_ERR(i2c->pdmabase);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq;
+
+ init_completion(&i2c->msg_complete);
+
+ of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
+ if (!of_id)
+ return -EINVAL;
+
+ i2c->dev_comp = of_id->data;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
+ i2c->dev = &pdev->dev;
+ i2c->adap.dev.parent = &pdev->dev;
+ i2c->adap.owner = THIS_MODULE;
+ i2c->adap.algo = &mtk_i2c_algorithm;
+ i2c->adap.quirks = i2c->dev_comp->quirks;
+ i2c->adap.timeout = 2 * HZ;
+ i2c->adap.retries = 1;
+
+ if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
+ return -EINVAL;
+
+ i2c->clk_main = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(i2c->clk_main)) {
+ dev_err(&pdev->dev, "cannot get main clock\n");
+ return PTR_ERR(i2c->clk_main);
+ }
+
+ i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
+ if (IS_ERR(i2c->clk_dma)) {
+ dev_err(&pdev->dev, "cannot get dma clock\n");
+ return PTR_ERR(i2c->clk_dma);
+ }
+
+ clk = i2c->clk_main;
+ if (i2c->have_pmic) {
+ i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
+ if (IS_ERR(i2c->clk_pmic)) {
+ dev_err(&pdev->dev, "cannot get pmic clock\n");
+ return PTR_ERR(i2c->clk_pmic);
+ }
+ clk = i2c->clk_pmic;
+ }
+
+ strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
+
+ ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set the speed.\n");
+ return -EINVAL;
+ }
+
+ ret = mtk_i2c_clock_enable(i2c);
+ if (ret) {
+ dev_err(&pdev->dev, "clock enable failed!\n");
+ return ret;
+ }
+ mtk_i2c_init_hw(i2c);
+ mtk_i2c_clock_disable(i2c);
+
+ ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
+ IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Request I2C IRQ %d fail\n", irq);
+ return ret;
+ }
+
+ i2c_set_adapdata(&i2c->adap, i2c);
+ ret = i2c_add_adapter(&i2c->adap);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, i2c);
+
+ return 0;
+}
+
+static int mtk_i2c_remove(struct platform_device *pdev)
+{
+ struct mtk_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+
+ return 0;
+}
+
+static struct platform_driver mtk_i2c_driver = {
+ .probe = mtk_i2c_probe,
+ .remove = mtk_i2c_remove,
+ .driver = {
+ .name = I2C_DRV_NAME,
+ .of_match_table = of_match_ptr(mtk_i2c_of_match),
+ },
+};
+
+module_platform_driver(mtk_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek I2C Bus Driver");
+MODULE_AUTHOR("Xudong Chen <xudong.chen@mediatek.com>");
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 3e84f6c..033846c 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -784,7 +784,7 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
return 0;
}
-static struct platform_device_id mxs_i2c_devtype[] = {
+static const struct platform_device_id mxs_i2c_devtype[] = {
{
.name = "imx23-i2c",
.driver_data = MXS_I2C_V1,
diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c
index 6e75e01..32914ab 100644
--- a/drivers/i2c/busses/i2c-octeon.c
+++ b/drivers/i2c/busses/i2c-octeon.c
@@ -200,7 +200,7 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c)
*/
static int octeon_i2c_wait(struct octeon_i2c *i2c)
{
- int result;
+ long result;
octeon_i2c_int_enable(i2c);
@@ -210,10 +210,7 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
octeon_i2c_int_disable(i2c);
- if (result < 0) {
- dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__);
- return result;
- } else if (result == 0) {
+ if (result == 0) {
dev_dbg(i2c->dev, "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 0e89419..d1c22e3 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/i2c-omap.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
/* I2C controller revisions */
#define OMAP_I2C_OMAP1_REV_2 0x20
@@ -481,10 +482,8 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
timeout = jiffies + OMAP_I2C_TIMEOUT;
while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
- if (time_after(jiffies, timeout)) {
- dev_warn(dev->dev, "timeout waiting for bus ready\n");
- return -ETIMEDOUT;
- }
+ if (time_after(jiffies, timeout))
+ return i2c_recover_bus(&dev->adapter);
msleep(1);
}
@@ -1209,6 +1208,68 @@ MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
#define OMAP_I2C_SCHEME_0 0
#define OMAP_I2C_SCHEME_1 1
+static int omap_i2c_get_scl(struct i2c_adapter *adap)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ u32 reg;
+
+ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+
+ return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC;
+}
+
+static int omap_i2c_get_sda(struct i2c_adapter *adap)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ u32 reg;
+
+ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+
+ return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC;
+}
+
+static void omap_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ u32 reg;
+
+ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ if (val)
+ reg |= OMAP_I2C_SYSTEST_SCL_O;
+ else
+ reg &= ~OMAP_I2C_SYSTEST_SCL_O;
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static void omap_i2c_prepare_recovery(struct i2c_adapter *adap)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ u32 reg;
+
+ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ reg |= OMAP_I2C_SYSTEST_ST_EN;
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static void omap_i2c_unprepare_recovery(struct i2c_adapter *adap)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ u32 reg;
+
+ reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ reg &= ~OMAP_I2C_SYSTEST_ST_EN;
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg);
+}
+
+static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = {
+ .get_scl = omap_i2c_get_scl,
+ .get_sda = omap_i2c_get_sda,
+ .set_scl = omap_i2c_set_scl,
+ .prepare_recovery = omap_i2c_prepare_recovery,
+ .unprepare_recovery = omap_i2c_unprepare_recovery,
+ .recover_bus = i2c_generic_scl_recovery,
+};
+
static int
omap_i2c_probe(struct platform_device *pdev)
{
@@ -1358,6 +1419,7 @@ omap_i2c_probe(struct platform_device *pdev)
adap->algo = &omap_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
+ adap->bus_recovery_info = &omap_i2c_bus_recovery_info;
/* i2c device drivers may be active on return from add_adapter() */
adap->nr = pdev->id;
@@ -1423,6 +1485,8 @@ static int omap_i2c_runtime_suspend(struct device *dev)
omap_i2c_read_reg(_dev, OMAP_I2C_STAT_REG);
}
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -1431,6 +1495,8 @@ static int omap_i2c_runtime_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
+ pinctrl_pm_select_default_state(dev);
+
if (!_dev->regs)
return 0;
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 5a84bea..d8361da 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -490,7 +490,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
struct device *dev = rcar_i2c_priv_to_dev(priv);
unsigned long flags;
- int i, ret, timeout;
+ int i, ret;
+ long timeout;
pm_runtime_get_sync(dev);
@@ -532,7 +533,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
timeout = wait_event_timeout(priv->wait,
rcar_i2c_flags_has(priv, ID_DONE),
- 5 * HZ);
+ adap->timeout);
if (!timeout) {
ret = -ETIMEDOUT;
break;
@@ -604,7 +605,8 @@ static int rcar_unreg_slave(struct i2c_client *slave)
static u32 rcar_i2c_func(struct i2c_adapter *adap)
{
/* This HW can't do SMBUS_QUICK and NOSTART */
- return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
+ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm rcar_i2c_algo = {
@@ -713,7 +715,7 @@ static int rcar_i2c_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id rcar_i2c_id_table[] = {
+static const struct platform_device_id rcar_i2c_id_table[] = {
{ "i2c-rcar", I2C_RCAR_GEN1 },
{ "i2c-rcar_gen1", I2C_RCAR_GEN1 },
{ "i2c-rcar_gen2", I2C_RCAR_GEN2 },
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 019d542..72e97e30 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -72,7 +72,7 @@ enum {
#define REG_INT_ALL 0x7f
/* Constants */
-#define WAIT_TIMEOUT 200 /* ms */
+#define WAIT_TIMEOUT 1000 /* ms */
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
enum rk3x_i2c_state {
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 297e9c9..50bfd8c 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -132,7 +132,7 @@ struct s3c24xx_i2c {
unsigned int sys_i2c_cfg;
};
-static struct platform_device_id s3c24xx_driver_ids[] = {
+static const struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = 0,
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 007818b..47659a9 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -150,6 +150,7 @@ struct sh_mobile_i2c_data {
struct sh_mobile_dt_config {
int clks_per_count;
+ void (*setup)(struct sh_mobile_i2c_data *pd);
};
#define IIC_FLAG_HAS_ICIC67 (1 << 0)
@@ -164,6 +165,7 @@ struct sh_mobile_dt_config {
#define ICIC 0x0c
#define ICCL 0x10
#define ICCH 0x14
+#define ICSTART 0x70
/* Register bits */
#define ICCR_ICE 0x80
@@ -190,6 +192,8 @@ struct sh_mobile_dt_config {
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+#define ICSTART_ICSTART 0x10
+
static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
{
if (offs == ICIC)
@@ -726,7 +730,8 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
struct i2c_msg *msg;
int err = 0;
- int i, k;
+ int i;
+ long timeout;
activate_ch(pd);
@@ -745,10 +750,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
i2c_op(pd, OP_START, 0);
/* The interrupt handler takes care of the rest... */
- k = wait_event_timeout(pd->wait,
+ timeout = wait_event_timeout(pd->wait,
pd->sr & (ICSR_TACK | SW_DONE),
- 5 * HZ);
- if (!k) {
+ adapter->timeout);
+ if (!timeout) {
dev_err(pd->dev, "Transfer request timed out\n");
if (pd->dma_direction != DMA_NONE)
sh_mobile_i2c_cleanup_dma(pd);
@@ -782,6 +787,33 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = {
.master_xfer = sh_mobile_i2c_xfer,
};
+/*
+ * r8a7740 chip has lasting errata on I2C I/O pad reset.
+ * this is work-around for it.
+ */
+static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
+{
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
+ iic_rd(pd, ICCR); /* dummy read */
+
+ iic_set_clr(pd, ICSTART, ICSTART_ICSTART, 0);
+ iic_rd(pd, ICSTART); /* dummy read */
+
+ udelay(10);
+
+ iic_wr(pd, ICCR, ICCR_SCP);
+ iic_wr(pd, ICSTART, 0);
+
+ udelay(10);
+
+ iic_wr(pd, ICCR, ICCR_TRS);
+ udelay(10);
+ iic_wr(pd, ICCR, 0);
+ udelay(10);
+ iic_wr(pd, ICCR, ICCR_TRS);
+ udelay(10);
+}
+
static const struct sh_mobile_dt_config default_dt_config = {
.clks_per_count = 1,
};
@@ -790,9 +822,15 @@ static const struct sh_mobile_dt_config fast_clock_dt_config = {
.clks_per_count = 2,
};
+static const struct sh_mobile_dt_config r8a7740_dt_config = {
+ .clks_per_count = 1,
+ .setup = sh_mobile_i2c_r8a7740_workaround,
+};
+
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
{ .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
@@ -885,6 +923,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
config = match->data;
pd->clks_per_count = config->clks_per_count;
+
+ if (config->setup)
+ config->setup(pd);
}
} else {
if (pdata && pdata->bus_speed)
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 1bcd75e..78a3668 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -656,8 +656,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
- u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_PROTOCOL_MANGLING;
+ u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+ I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
if (i2c_dev->hw->has_continue_xfer_support)
ret |= I2C_FUNC_NOSTART;
@@ -669,6 +669,12 @@ static const struct i2c_algorithm tegra_i2c_algo = {
.functionality = tegra_i2c_func,
};
+/* payload size is only 12 bit */
+static struct i2c_adapter_quirks tegra_i2c_quirks = {
+ .max_read_len = 4096,
+ .max_write_len = 4096,
+};
+
static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_continue_xfer_support = false,
.has_per_pkt_xfer_complete_irq = false,
@@ -739,6 +745,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->base = base;
i2c_dev->div_clk = div_clk;
i2c_dev->adapter.algo = &tegra_i2c_algo;
+ i2c_dev->adapter.quirks = &tegra_i2c_quirks;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c
new file mode 100644
index 0000000..dcca707
--- /dev/null
+++ b/drivers/i2c/busses/i2c-xgene-slimpro.c
@@ -0,0 +1,469 @@
+/*
+ * X-Gene SLIMpro I2C Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan@apm.com>
+ * Author: Hieu Le <hnle@apm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver provides support for X-Gene SLIMpro I2C device access
+ * using the APM X-Gene SLIMpro mailbox driver.
+ *
+ */
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */
+#define MAILBOX_I2C_INDEX 0
+#define SLIMPRO_IIC_BUS 1 /* Use I2C bus 1 only */
+
+#define SMBUS_CMD_LEN 1
+#define BYTE_DATA 1
+#define WORD_DATA 2
+#define BLOCK_DATA 3
+
+#define SLIMPRO_IIC_I2C_PROTOCOL 0
+#define SLIMPRO_IIC_SMB_PROTOCOL 1
+
+#define SLIMPRO_IIC_READ 0
+#define SLIMPRO_IIC_WRITE 1
+
+#define IIC_SMB_WITHOUT_DATA_LEN 0
+#define IIC_SMB_WITH_DATA_LEN 1
+
+#define SLIMPRO_DEBUG_MSG 0
+#define SLIMPRO_MSG_TYPE_SHIFT 28
+#define SLIMPRO_DBG_SUBTYPE_I2C1READ 4
+#define SLIMPRO_DBGMSG_TYPE_SHIFT 24
+#define SLIMPRO_DBGMSG_TYPE_MASK 0x0F000000U
+#define SLIMPRO_IIC_DEV_SHIFT 23
+#define SLIMPRO_IIC_DEV_MASK 0x00800000U
+#define SLIMPRO_IIC_DEVID_SHIFT 13
+#define SLIMPRO_IIC_DEVID_MASK 0x007FE000U
+#define SLIMPRO_IIC_RW_SHIFT 12
+#define SLIMPRO_IIC_RW_MASK 0x00001000U
+#define SLIMPRO_IIC_PROTO_SHIFT 11
+#define SLIMPRO_IIC_PROTO_MASK 0x00000800U
+#define SLIMPRO_IIC_ADDRLEN_SHIFT 8
+#define SLIMPRO_IIC_ADDRLEN_MASK 0x00000700U
+#define SLIMPRO_IIC_DATALEN_SHIFT 0
+#define SLIMPRO_IIC_DATALEN_MASK 0x000000FFU
+
+/*
+ * SLIMpro I2C message encode
+ *
+ * dev - Controller number (0-based)
+ * chip - I2C chip address
+ * op - SLIMPRO_IIC_READ or SLIMPRO_IIC_WRITE
+ * proto - SLIMPRO_IIC_SMB_PROTOCOL or SLIMPRO_IIC_I2C_PROTOCOL
+ * addrlen - Length of the address field
+ * datalen - Length of the data field
+ */
+#define SLIMPRO_IIC_ENCODE_MSG(dev, chip, op, proto, addrlen, datalen) \
+ ((SLIMPRO_DEBUG_MSG << SLIMPRO_MSG_TYPE_SHIFT) | \
+ ((SLIMPRO_DBG_SUBTYPE_I2C1READ << SLIMPRO_DBGMSG_TYPE_SHIFT) & \
+ SLIMPRO_DBGMSG_TYPE_MASK) | \
+ ((dev << SLIMPRO_IIC_DEV_SHIFT) & SLIMPRO_IIC_DEV_MASK) | \
+ ((chip << SLIMPRO_IIC_DEVID_SHIFT) & SLIMPRO_IIC_DEVID_MASK) | \
+ ((op << SLIMPRO_IIC_RW_SHIFT) & SLIMPRO_IIC_RW_MASK) | \
+ ((proto << SLIMPRO_IIC_PROTO_SHIFT) & SLIMPRO_IIC_PROTO_MASK) | \
+ ((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_MASK) | \
+ ((datalen << SLIMPRO_IIC_DATALEN_SHIFT) & SLIMPRO_IIC_DATALEN_MASK))
+
+/*
+ * Encode for upper address for block data
+ */
+#define SLIMPRO_IIC_ENCODE_FLAG_BUFADDR 0x80000000
+#define SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(a) ((u32) (((a) << 30) \
+ & 0x40000000))
+#define SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(a) ((u32) (((a) >> 12) \
+ & 0x3FF00000))
+#define SLIMPRO_IIC_ENCODE_ADDR(a) ((a) & 0x000FFFFF)
+
+struct slimpro_i2c_dev {
+ struct i2c_adapter adapter;
+ struct device *dev;
+ struct mbox_chan *mbox_chan;
+ struct mbox_client mbox_client;
+ struct completion rd_complete;
+ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX];
+ u32 *resp_msg;
+};
+
+#define to_slimpro_i2c_dev(cl) \
+ container_of(cl, struct slimpro_i2c_dev, mbox_client)
+
+static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg)
+{
+ struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl);
+
+ /*
+ * Response message format:
+ * mssg[0] is the return code of the operation
+ * mssg[1] is the first data word
+ * mssg[2] is NOT used
+ */
+ if (ctx->resp_msg)
+ *ctx->resp_msg = ((u32 *)mssg)[1];
+
+ if (ctx->mbox_client.tx_block)
+ complete(&ctx->rd_complete);
+}
+
+static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx)
+{
+ if (ctx->mbox_client.tx_block) {
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ msecs_to_jiffies(MAILBOX_OP_TIMEOUT)))
+ return -ETIMEDOUT;
+ }
+
+ /* Check of invalid data or no device */
+ if (*ctx->resp_msg == 0xffffffff)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip,
+ u32 addr, u32 addrlen, u32 protocol,
+ u32 readlen, u32 *data)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip,
+ SLIMPRO_IIC_READ, protocol, addrlen, readlen);
+ msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr);
+ msg[2] = 0;
+ ctx->resp_msg = data;
+ rc = mbox_send_message(ctx->mbox_chan, &msg);
+ if (rc < 0)
+ goto err;
+
+ rc = start_i2c_msg_xfer(ctx);
+err:
+ ctx->resp_msg = NULL;
+ return rc;
+}
+
+static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip,
+ u32 addr, u32 addrlen, u32 protocol, u32 writelen,
+ u32 data)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip,
+ SLIMPRO_IIC_WRITE, protocol, addrlen, writelen);
+ msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr);
+ msg[2] = data;
+ ctx->resp_msg = msg;
+
+ rc = mbox_send_message(ctx->mbox_chan, &msg);
+ if (rc < 0)
+ goto err;
+
+ rc = start_i2c_msg_xfer(ctx);
+err:
+ ctx->resp_msg = NULL;
+ return rc;
+}
+
+static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr,
+ u32 addrlen, u32 protocol, u32 readlen,
+ u32 with_data_len, void *data)
+{
+ dma_addr_t paddr;
+ u32 msg[3];
+ int rc;
+
+ paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE);
+ rc = dma_mapping_error(ctx->dev, paddr);
+ if (rc) {
+ dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
+ ctx->dma_buffer);
+ goto err;
+ }
+
+ msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ,
+ protocol, addrlen, readlen);
+ msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR |
+ SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(with_data_len) |
+ SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) |
+ SLIMPRO_IIC_ENCODE_ADDR(addr);
+ msg[2] = (u32)paddr;
+ ctx->resp_msg = msg;
+
+ rc = mbox_send_message(ctx->mbox_chan, &msg);
+ if (rc < 0)
+ goto err_unmap;
+
+ rc = start_i2c_msg_xfer(ctx);
+
+ /* Copy to destination */
+ memcpy(data, ctx->dma_buffer, readlen);
+
+err_unmap:
+ dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE);
+err:
+ ctx->resp_msg = NULL;
+ return rc;
+}
+
+static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip,
+ u32 addr, u32 addrlen, u32 protocol, u32 writelen,
+ void *data)
+{
+ dma_addr_t paddr;
+ u32 msg[3];
+ int rc;
+
+ memcpy(ctx->dma_buffer, data, writelen);
+ paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen,
+ DMA_TO_DEVICE);
+ rc = dma_mapping_error(ctx->dev, paddr);
+ if (rc) {
+ dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
+ ctx->dma_buffer);
+ goto err;
+ }
+
+ msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE,
+ protocol, addrlen, writelen);
+ msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR |
+ SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) |
+ SLIMPRO_IIC_ENCODE_ADDR(addr);
+ msg[2] = (u32)paddr;
+ ctx->resp_msg = msg;
+
+ if (ctx->mbox_client.tx_block)
+ reinit_completion(&ctx->rd_complete);
+
+ rc = mbox_send_message(ctx->mbox_chan, &msg);
+ if (rc < 0)
+ goto err_unmap;
+
+ rc = start_i2c_msg_xfer(ctx);
+
+err_unmap:
+ dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE);
+err:
+ ctx->resp_msg = NULL;
+ return rc;
+}
+
+static int xgene_slimpro_i2c_xfer(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ struct slimpro_i2c_dev *ctx = i2c_get_adapdata(adap);
+ int ret = -EOPNOTSUPP;
+ u32 val;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_READ) {
+ ret = slimpro_i2c_rd(ctx, addr, 0, 0,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ BYTE_DATA, &val);
+ data->byte = val;
+ } else {
+ ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ 0, 0);
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ BYTE_DATA, &val);
+ data->byte = val;
+ } else {
+ val = data->byte;
+ ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ BYTE_DATA, val);
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ WORD_DATA, &val);
+ data->word = val;
+ } else {
+ val = data->word;
+ ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ WORD_DATA, val);
+ }
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ ret = slimpro_i2c_blkrd(ctx, addr, command,
+ SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ I2C_SMBUS_BLOCK_MAX + 1,
+ IIC_SMB_WITH_DATA_LEN,
+ &data->block[0]);
+
+ } else {
+ ret = slimpro_i2c_blkwr(ctx, addr, command,
+ SMBUS_CMD_LEN,
+ SLIMPRO_IIC_SMB_PROTOCOL,
+ data->block[0] + 1,
+ &data->block[0]);
+ }
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (read_write == I2C_SMBUS_READ) {
+ ret = slimpro_i2c_blkrd(ctx, addr,
+ command,
+ SMBUS_CMD_LEN,
+ SLIMPRO_IIC_I2C_PROTOCOL,
+ I2C_SMBUS_BLOCK_MAX,
+ IIC_SMB_WITHOUT_DATA_LEN,
+ &data->block[1]);
+ } else {
+ ret = slimpro_i2c_blkwr(ctx, addr, command,
+ SMBUS_CMD_LEN,
+ SLIMPRO_IIC_I2C_PROTOCOL,
+ data->block[0],
+ &data->block[1]);
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/*
+* Return list of supported functionality.
+*/
+static u32 xgene_slimpro_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static struct i2c_algorithm xgene_slimpro_i2c_algorithm = {
+ .smbus_xfer = xgene_slimpro_i2c_xfer,
+ .functionality = xgene_slimpro_i2c_func,
+};
+
+static int xgene_slimpro_i2c_probe(struct platform_device *pdev)
+{
+ struct slimpro_i2c_dev *ctx;
+ struct i2c_adapter *adapter;
+ struct mbox_client *cl;
+ int rc;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ctx);
+ cl = &ctx->mbox_client;
+
+ /* Request mailbox channel */
+ cl->dev = &pdev->dev;
+ cl->rx_callback = slimpro_i2c_rx_cb;
+ cl->tx_block = true;
+ init_completion(&ctx->rd_complete);
+ cl->tx_tout = MAILBOX_OP_TIMEOUT;
+ cl->knows_txdone = false;
+ ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev, "i2c mailbox channel request failed\n");
+ return PTR_ERR(ctx->mbox_chan);
+ }
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc)
+ dev_warn(&pdev->dev, "Unable to set dma mask\n");
+
+ /* Setup I2C adapter */
+ adapter = &ctx->adapter;
+ snprintf(adapter->name, sizeof(adapter->name), "MAILBOX I2C");
+ adapter->algo = &xgene_slimpro_i2c_algorithm;
+ adapter->class = I2C_CLASS_HWMON;
+ adapter->dev.parent = &pdev->dev;
+ i2c_set_adapdata(adapter, ctx);
+ rc = i2c_add_adapter(adapter);
+ if (rc) {
+ dev_err(&pdev->dev, "Adapter registeration failed\n");
+ return rc;
+ }
+
+ dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n");
+ return 0;
+}
+
+static int xgene_slimpro_i2c_remove(struct platform_device *pdev)
+{
+ struct slimpro_i2c_dev *ctx = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&ctx->adapter);
+
+ mbox_free_channel(ctx->mbox_chan);
+
+ return 0;
+}
+
+static const struct of_device_id xgene_slimpro_i2c_dt_ids[] = {
+ {.compatible = "apm,xgene-slimpro-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = {
+ {"APMC0D40", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids);
+#endif
+
+static struct platform_driver xgene_slimpro_i2c_driver = {
+ .probe = xgene_slimpro_i2c_probe,
+ .remove = xgene_slimpro_i2c_remove,
+ .driver = {
+ .name = "xgene-slimpro-i2c",
+ .of_match_table = of_match_ptr(xgene_slimpro_i2c_dt_ids),
+ .acpi_match_table = ACPI_PTR(xgene_slimpro_i2c_acpi_ids)
+ },
+};
+
+module_platform_driver(xgene_slimpro_i2c_driver);
+
+MODULE_DESCRIPTION("APM X-Gene SLIMpro I2C driver");
+MODULE_AUTHOR("Feng Kan <fkan@apm.com>");
+MODULE_AUTHOR("Hieu Le <hnle@apm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index e840004..4dda23f 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -63,6 +63,7 @@ enum xiic_endian {
* @state: See STATE_
* @rx_msg: Current RX message
* @rx_pos: Position within current RX message
+ * @endianness: big/little-endian byte order
*/
struct xiic_i2c {
void __iomem *base;
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index fc2ee82..069a41f 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -257,7 +257,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
struct acpi_connection_info *info = &data->info;
struct acpi_resource_i2c_serialbus *sb;
struct i2c_adapter *adapter = data->adapter;
- struct i2c_client client;
+ struct i2c_client *client;
struct acpi_resource *ares;
u32 accessor_type = function >> 16;
u8 action = function & ACPI_IO_MASK;
@@ -268,6 +268,12 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
if (ACPI_FAILURE(ret))
return ret;
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client) {
+ ret = AE_NO_MEMORY;
+ goto err;
+ }
+
if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
ret = AE_BAD_PARAMETER;
goto err;
@@ -279,75 +285,73 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
goto err;
}
- memset(&client, 0, sizeof(client));
- client.adapter = adapter;
- client.addr = sb->slave_address;
- client.flags = 0;
+ client->adapter = adapter;
+ client->addr = sb->slave_address;
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
- client.flags |= I2C_CLIENT_TEN;
+ client->flags |= I2C_CLIENT_TEN;
switch (accessor_type) {
case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV:
if (action == ACPI_READ) {
- status = i2c_smbus_read_byte(&client);
+ status = i2c_smbus_read_byte(client);
if (status >= 0) {
gsb->bdata = status;
status = 0;
}
} else {
- status = i2c_smbus_write_byte(&client, gsb->bdata);
+ status = i2c_smbus_write_byte(client, gsb->bdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_BYTE:
if (action == ACPI_READ) {
- status = i2c_smbus_read_byte_data(&client, command);
+ status = i2c_smbus_read_byte_data(client, command);
if (status >= 0) {
gsb->bdata = status;
status = 0;
}
} else {
- status = i2c_smbus_write_byte_data(&client, command,
+ status = i2c_smbus_write_byte_data(client, command,
gsb->bdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_WORD:
if (action == ACPI_READ) {
- status = i2c_smbus_read_word_data(&client, command);
+ status = i2c_smbus_read_word_data(client, command);
if (status >= 0) {
gsb->wdata = status;
status = 0;
}
} else {
- status = i2c_smbus_write_word_data(&client, command,
+ status = i2c_smbus_write_word_data(client, command,
gsb->wdata);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_BLOCK:
if (action == ACPI_READ) {
- status = i2c_smbus_read_block_data(&client, command,
+ status = i2c_smbus_read_block_data(client, command,
gsb->data);
if (status >= 0) {
gsb->len = status;
status = 0;
}
} else {
- status = i2c_smbus_write_block_data(&client, command,
+ status = i2c_smbus_write_block_data(client, command,
gsb->len, gsb->data);
}
break;
case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE:
if (action == ACPI_READ) {
- status = acpi_gsb_i2c_read_bytes(&client, command,
+ status = acpi_gsb_i2c_read_bytes(client, command,
gsb->data, info->access_length);
if (status > 0)
status = 0;
} else {
- status = acpi_gsb_i2c_write_bytes(&client, command,
+ status = acpi_gsb_i2c_write_bytes(client, command,
gsb->data, info->access_length);
}
break;
@@ -361,6 +365,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
gsb->status = status;
err:
+ kfree(client);
ACPI_FREE(ares);
return ret;
}
@@ -1276,7 +1281,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
}
addr = of_get_property(node, "reg", &len);
- if (!addr || (len < sizeof(int))) {
+ if (!addr || (len < sizeof(*addr))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
@@ -1677,7 +1682,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)
* FIXME: This is old code and should ideally be replaced by an
* alternative which results in decoupling the lifetime of the struct
* device from the i2c_adapter, like spi or netdev do. Any solution
- * should be throughly tested with DEBUG_KOBJECT_RELEASE enabled!
+ * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled!
*/
init_completion(&adap->dev_released);
device_unregister(&adap->dev);
@@ -2918,18 +2923,24 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
{
int ret;
- if (!client || !slave_cb)
+ if (!client || !slave_cb) {
+ WARN(1, "insufficent data\n");
return -EINVAL;
+ }
if (!(client->flags & I2C_CLIENT_TEN)) {
/* Enforce stricter address checking */
ret = i2c_check_addr_validity(client->addr);
- if (ret)
+ if (ret) {
+ dev_err(&client->dev, "%s: invalid address\n", __func__);
return ret;
+ }
}
- if (!client->adapter->algo->reg_slave)
+ if (!client->adapter->algo->reg_slave) {
+ dev_err(&client->dev, "%s: not supported by adapter\n", __func__);
return -EOPNOTSUPP;
+ }
client->slave_cb = slave_cb;
@@ -2937,8 +2948,10 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
ret = client->adapter->algo->reg_slave(client);
i2c_unlock_adapter(client->adapter);
- if (ret)
+ if (ret) {
client->slave_cb = NULL;
+ dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret);
+ }
return ret;
}
@@ -2948,8 +2961,10 @@ int i2c_slave_unregister(struct i2c_client *client)
{
int ret;
- if (!client->adapter->algo->unreg_slave)
+ if (!client->adapter->algo->unreg_slave) {
+ dev_err(&client->dev, "%s: not supported by adapter\n", __func__);
return -EOPNOTSUPP;
+ }
i2c_lock_adapter(client->adapter);
ret = client->adapter->algo->unreg_slave(client);
@@ -2957,6 +2972,8 @@ int i2c_slave_unregister(struct i2c_client *client)
if (ret == 0)
client->slave_cb = NULL;
+ else
+ dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret);
return ret;
}
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 06cc1ff..2ba7c0f 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -51,7 +51,7 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
- ret = parent->algo->master_xfer(parent, msgs, num);
+ ret = __i2c_transfer(parent, msgs, num);
if (priv->deselect)
priv->deselect(parent, priv->mux_priv, priv->chan_id);
@@ -144,6 +144,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
priv->adap.dev.parent = &parent->dev;
priv->adap.retries = parent->retries;
priv->adap.timeout = parent->timeout;
+ priv->adap.quirks = parent->quirks;
/* Sanity check on class */
if (i2c_mux_parent_classes(parent) & class)
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index 9ebf9cb..94765a8 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -89,7 +89,7 @@ static void smbus_alert(struct work_struct *work)
* to high, because of slave transmit arbitration. After
* responding, an SMBus device stops asserting SMBALERT#.
*
- * Note that SMBus 2.0 reserves 10-bit addresess for future
+ * Note that SMBus 2.0 reserves 10-bit addresses for future
* use. We neither handle them, nor try to use PEC here.
*/
status = i2c_smbus_read_byte(ara);
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f6d313e..fdd0769 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -7,7 +7,8 @@ menu "Multiplexer I2C Chip support"
config I2C_ARB_GPIO_CHALLENGE
tristate "GPIO-based I2C arbitration"
- depends on GPIOLIB && OF
+ depends on GPIOLIB || COMPILE_TEST
+ depends on OF
help
If you say yes to this option, support will be included for an
I2C multimaster arbitration scheme using GPIOs and a challenge &
@@ -40,7 +41,7 @@ config I2C_MUX_PCA9541
config I2C_MUX_PCA954x
tristate "Philips PCA954x I2C Mux/switches"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
If you say yes here you get support for the Philips PCA954x
I2C mux/switch devices.
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
index cb77277..0c8d4d2 100644
--- a/drivers/i2c/muxes/i2c-mux-pca9541.c
+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
@@ -104,7 +104,7 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
buf[0] = command;
buf[1] = val;
msg.buf = buf;
- ret = adap->algo->master_xfer(adap, &msg, 1);
+ ret = __i2c_transfer(adap, &msg, 1);
} else {
union i2c_smbus_data data;
@@ -144,7 +144,7 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command)
.buf = &val
}
};
- ret = adap->algo->master_xfer(adap, msg, 2);
+ ret = __i2c_transfer(adap, msg, 2);
if (ret == 2)
ret = val;
else if (ret >= 0)
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index bea0d2d..ea4aa9d 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -134,7 +134,7 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
msg.len = 1;
buf[0] = val;
msg.buf = buf;
- ret = adap->algo->master_xfer(adap, &msg, 1);
+ ret = __i2c_transfer(adap, &msg, 1);
} else {
union i2c_smbus_data data;
ret = adap->algo->smbus_xfer(adap, client->addr,
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index fac3d9d..1362ad8 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -93,7 +93,7 @@ int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk,
int error;
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->special = (char *)pc;
if (buf && bufflen) {
@@ -191,7 +191,7 @@ void ide_prep_sense(ide_drive_t *drive, struct request *rq)
BUG_ON(sense_len > sizeof(*sense));
- if (rq->cmd_type == REQ_TYPE_SENSE || drive->sense_rq_armed)
+ if (rq->cmd_type == REQ_TYPE_ATA_SENSE || drive->sense_rq_armed)
return;
memset(sense, 0, sizeof(*sense));
@@ -210,7 +210,7 @@ void ide_prep_sense(ide_drive_t *drive, struct request *rq)
sense_rq->rq_disk = rq->rq_disk;
sense_rq->cmd[0] = GPCMD_REQUEST_SENSE;
sense_rq->cmd[4] = cmd_len;
- sense_rq->cmd_type = REQ_TYPE_SENSE;
+ sense_rq->cmd_type = REQ_TYPE_ATA_SENSE;
sense_rq->cmd_flags |= REQ_PREEMPT;
if (drive->media == ide_tape)
@@ -310,7 +310,7 @@ int ide_cd_get_xferlen(struct request *rq)
switch (rq->cmd_type) {
case REQ_TYPE_FS:
return 32768;
- case REQ_TYPE_SENSE:
+ case REQ_TYPE_ATA_SENSE:
case REQ_TYPE_BLOCK_PC:
case REQ_TYPE_ATA_PC:
return blk_rq_bytes(rq);
@@ -477,7 +477,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
if (uptodate == 0)
drive->failed_pc = NULL;
- if (rq->cmd_type == REQ_TYPE_SPECIAL) {
+ if (rq->cmd_type == REQ_TYPE_DRV_PRIV) {
rq->errors = 0;
error = 0;
} else {
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 0b510ba..64a6b82 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -210,7 +210,7 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive,
static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq)
{
/*
- * For REQ_TYPE_SENSE, "rq->special" points to the original
+ * For REQ_TYPE_ATA_SENSE, "rq->special" points to the original
* failed request. Also, the sense data should be read
* directly from rq which might be different from the original
* sense buffer if it got copied during mapping.
@@ -285,7 +285,7 @@ static int cdrom_decode_status(ide_drive_t *drive, u8 stat)
"stat 0x%x",
rq->cmd[0], rq->cmd_type, err, stat);
- if (rq->cmd_type == REQ_TYPE_SENSE) {
+ if (rq->cmd_type == REQ_TYPE_ATA_SENSE) {
/*
* We got an error trying to get sense info from the drive
* (probably while trying to recover from a former error).
@@ -526,7 +526,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, thislen, uptodate = 0;
int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0;
- int sense = (rq->cmd_type == REQ_TYPE_SENSE);
+ int sense = (rq->cmd_type == REQ_TYPE_ATA_SENSE);
unsigned int timeout;
u16 len;
u8 ireason, stat;
@@ -791,7 +791,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
if (cdrom_start_rw(drive, rq) == ide_stopped)
goto out_end;
break;
- case REQ_TYPE_SENSE:
+ case REQ_TYPE_ATA_SENSE:
case REQ_TYPE_BLOCK_PC:
case REQ_TYPE_ATA_PC:
if (!rq->timeout)
@@ -799,7 +799,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
cdrom_do_block_pc(drive, rq);
break;
- case REQ_TYPE_SPECIAL:
+ case REQ_TYPE_DRV_PRIV:
/* right now this can only be a reset... */
uptodate = 1;
goto out_end;
diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c
index 02caa7d..066e390 100644
--- a/drivers/ide/ide-cd_ioctl.c
+++ b/drivers/ide/ide-cd_ioctl.c
@@ -304,7 +304,7 @@ int ide_cdrom_reset(struct cdrom_device_info *cdi)
int ret;
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->cmd_flags = REQ_QUIET;
ret = blk_execute_rq(drive->queue, cd->disk, rq, 0);
blk_put_request(rq);
diff --git a/drivers/ide/ide-devsets.c b/drivers/ide/ide-devsets.c
index 9e98122..b05a74d 100644
--- a/drivers/ide/ide-devsets.c
+++ b/drivers/ide/ide-devsets.c
@@ -166,7 +166,7 @@ int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
return setting->set(drive, arg);
rq = blk_get_request(q, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->cmd_len = 5;
rq->cmd[0] = REQ_DEVSET_EXEC;
*(int *)&rq->cmd[1] = arg;
diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c
index 3297066..d6da011 100644
--- a/drivers/ide/ide-eh.c
+++ b/drivers/ide/ide-eh.c
@@ -129,7 +129,7 @@ ide_startstop_t ide_error(ide_drive_t *drive, const char *msg, u8 stat)
if (cmd)
ide_complete_cmd(drive, cmd, stat, err);
- } else if (blk_pm_request(rq)) {
+ } else if (ata_pm_request(rq)) {
rq->errors = 1;
ide_complete_pm_rq(drive, rq);
return ide_stopped;
@@ -147,7 +147,7 @@ static inline void ide_complete_drive_reset(ide_drive_t *drive, int err)
{
struct request *rq = drive->hwif->rq;
- if (rq && rq->cmd_type == REQ_TYPE_SPECIAL &&
+ if (rq && rq->cmd_type == REQ_TYPE_DRV_PRIV &&
rq->cmd[0] == REQ_DRIVE_RESET) {
if (err <= 0 && rq->errors == 0)
rq->errors = -EIO;
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 8c6363c..2fb5350 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -97,7 +97,7 @@ static int ide_floppy_callback(ide_drive_t *drive, int dsc)
"Aborting request!\n");
}
- if (rq->cmd_type == REQ_TYPE_SPECIAL)
+ if (rq->cmd_type == REQ_TYPE_DRV_PRIV)
rq->errors = uptodate ? 0 : IDE_DRV_ERROR_GENERAL;
return uptodate;
@@ -246,7 +246,7 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
} else
printk(KERN_ERR PFX "%s: I/O error\n", drive->name);
- if (rq->cmd_type == REQ_TYPE_SPECIAL) {
+ if (rq->cmd_type == REQ_TYPE_DRV_PRIV) {
rq->errors = 0;
ide_complete_rq(drive, 0, blk_rq_bytes(rq));
return ide_stopped;
@@ -265,8 +265,8 @@ static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive,
pc = &floppy->queued_pc;
idefloppy_create_rw_cmd(drive, pc, rq, (unsigned long)block);
break;
- case REQ_TYPE_SPECIAL:
- case REQ_TYPE_SENSE:
+ case REQ_TYPE_DRV_PRIV:
+ case REQ_TYPE_ATA_SENSE:
pc = (struct ide_atapi_pc *)rq->special;
break;
case REQ_TYPE_BLOCK_PC:
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index 177db6d..669ea1e 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -135,7 +135,7 @@ EXPORT_SYMBOL(ide_complete_rq);
void ide_kill_rq(ide_drive_t *drive, struct request *rq)
{
- u8 drv_req = (rq->cmd_type == REQ_TYPE_SPECIAL) && rq->rq_disk;
+ u8 drv_req = (rq->cmd_type == REQ_TYPE_DRV_PRIV) && rq->rq_disk;
u8 media = drive->media;
drive->failed_pc = NULL;
@@ -320,7 +320,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
goto kill_rq;
}
- if (blk_pm_request(rq))
+ if (ata_pm_request(rq))
ide_check_pm_state(drive, rq);
drive->hwif->tp_ops->dev_select(drive);
@@ -342,8 +342,8 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
return execute_drive_cmd(drive, rq);
- else if (blk_pm_request(rq)) {
- struct request_pm_state *pm = rq->special;
+ else if (ata_pm_request(rq)) {
+ struct ide_pm_state *pm = rq->special;
#ifdef DEBUG_PM
printk("%s: start_power_step(step: %d)\n",
drive->name, pm->pm_step);
@@ -353,7 +353,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
pm->pm_step == IDE_PM_COMPLETED)
ide_complete_pm_rq(drive, rq);
return startstop;
- } else if (!rq->rq_disk && rq->cmd_type == REQ_TYPE_SPECIAL)
+ } else if (!rq->rq_disk && rq->cmd_type == REQ_TYPE_DRV_PRIV)
/*
* TODO: Once all ULDs have been modified to
* check for specific op codes rather than
@@ -538,7 +538,7 @@ repeat:
* state machine.
*/
if ((drive->dev_flags & IDE_DFLAG_BLOCKED) &&
- blk_pm_request(rq) == 0 &&
+ ata_pm_request(rq) == 0 &&
(rq->cmd_flags & REQ_PREEMPT) == 0) {
/* there should be no pending command at this point */
ide_unlock_port(hwif);
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
index 6233fa2c..aa2e9b7 100644
--- a/drivers/ide/ide-ioctls.c
+++ b/drivers/ide/ide-ioctls.c
@@ -222,7 +222,7 @@ static int generic_drive_reset(ide_drive_t *drive)
int ret = 0;
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->cmd_len = 1;
rq->cmd[0] = REQ_DRIVE_RESET;
if (blk_execute_rq(drive->queue, NULL, rq, 1))
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c
index ca95860..c808685 100644
--- a/drivers/ide/ide-park.c
+++ b/drivers/ide/ide-park.c
@@ -34,7 +34,7 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
rq = blk_get_request(q, READ, __GFP_WAIT);
rq->cmd[0] = REQ_PARK_HEADS;
rq->cmd_len = 1;
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->special = &timeout;
rc = blk_execute_rq(q, NULL, rq, 1);
blk_put_request(rq);
@@ -51,7 +51,7 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
rq->cmd[0] = REQ_UNPARK_HEADS;
rq->cmd_len = 1;
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
elv_add_request(q, rq, ELEVATOR_INSERT_FRONT);
out:
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
index 8d1e32d..081e434 100644
--- a/drivers/ide/ide-pm.c
+++ b/drivers/ide/ide-pm.c
@@ -8,7 +8,7 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
ide_drive_t *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
- struct request_pm_state rqpm;
+ struct ide_pm_state rqpm;
int ret;
if (ide_port_acpi(hwif)) {
@@ -19,7 +19,7 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
memset(&rqpm, 0, sizeof(rqpm));
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_SUSPEND;
+ rq->cmd_type = REQ_TYPE_ATA_PM_SUSPEND;
rq->special = &rqpm;
rqpm.pm_step = IDE_PM_START_SUSPEND;
if (mesg.event == PM_EVENT_PRETHAW)
@@ -38,13 +38,43 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
return ret;
}
+static void ide_end_sync_rq(struct request *rq, int error)
+{
+ complete(rq->end_io_data);
+}
+
+static int ide_pm_execute_rq(struct request *rq)
+{
+ struct request_queue *q = rq->q;
+ DECLARE_COMPLETION_ONSTACK(wait);
+
+ rq->end_io_data = &wait;
+ rq->end_io = ide_end_sync_rq;
+
+ spin_lock_irq(q->queue_lock);
+ if (unlikely(blk_queue_dying(q))) {
+ rq->cmd_flags |= REQ_QUIET;
+ rq->errors = -ENXIO;
+ __blk_end_request_all(rq, rq->errors);
+ spin_unlock_irq(q->queue_lock);
+ return -ENXIO;
+ }
+ __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT);
+ __blk_run_queue_uncond(q);
+ spin_unlock_irq(q->queue_lock);
+
+ wait_for_completion_io(&wait);
+
+ return rq->errors ? -EIO : 0;
+}
+
int generic_ide_resume(struct device *dev)
{
ide_drive_t *drive = to_ide_device(dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
- struct request_pm_state rqpm;
+ struct ide_pm_state rqpm;
int err;
if (ide_port_acpi(hwif)) {
@@ -59,13 +89,13 @@ int generic_ide_resume(struct device *dev)
memset(&rqpm, 0, sizeof(rqpm));
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_RESUME;
+ rq->cmd_type = REQ_TYPE_ATA_PM_RESUME;
rq->cmd_flags |= REQ_PREEMPT;
rq->special = &rqpm;
rqpm.pm_step = IDE_PM_START_RESUME;
rqpm.pm_state = PM_EVENT_ON;
- err = blk_execute_rq(drive->queue, NULL, rq, 1);
+ err = ide_pm_execute_rq(rq);
blk_put_request(rq);
if (err == 0 && dev->driver) {
@@ -80,7 +110,7 @@ int generic_ide_resume(struct device *dev)
void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->special;
+ struct ide_pm_state *pm = rq->special;
#ifdef DEBUG_PM
printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
@@ -110,7 +140,7 @@ void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->special;
+ struct ide_pm_state *pm = rq->special;
struct ide_cmd cmd = { };
switch (pm->pm_step) {
@@ -182,7 +212,7 @@ out_do_tf:
void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
{
struct request_queue *q = drive->queue;
- struct request_pm_state *pm = rq->special;
+ struct ide_pm_state *pm = rq->special;
unsigned long flags;
ide_complete_power_step(drive, rq);
@@ -191,10 +221,10 @@ void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
#ifdef DEBUG_PM
printk("%s: completing PM request, %s\n", drive->name,
- (rq->cmd_type == REQ_TYPE_PM_SUSPEND) ? "suspend" : "resume");
+ (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND) ? "suspend" : "resume");
#endif
spin_lock_irqsave(q->queue_lock, flags);
- if (rq->cmd_type == REQ_TYPE_PM_SUSPEND)
+ if (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND)
blk_stop_queue(q);
else
drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
@@ -208,13 +238,13 @@ void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq)
void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->special;
+ struct ide_pm_state *pm = rq->special;
- if (rq->cmd_type == REQ_TYPE_PM_SUSPEND &&
+ if (rq->cmd_type == REQ_TYPE_ATA_PM_SUSPEND &&
pm->pm_step == IDE_PM_START_SUSPEND)
/* Mark drive blocked when starting the suspend sequence. */
drive->dev_flags |= IDE_DFLAG_BLOCKED;
- else if (rq->cmd_type == REQ_TYPE_PM_RESUME &&
+ else if (rq->cmd_type == REQ_TYPE_ATA_PM_RESUME &&
pm->pm_step == IDE_PM_START_RESUME) {
/*
* The first thing we do on wakeup is to wait for BSY bit to
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 6eb738c..f5d51d1 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -576,8 +576,8 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
rq->cmd[0], (unsigned long long)blk_rq_pos(rq),
blk_rq_sectors(rq));
- BUG_ON(!(rq->cmd_type == REQ_TYPE_SPECIAL ||
- rq->cmd_type == REQ_TYPE_SENSE));
+ BUG_ON(!(rq->cmd_type == REQ_TYPE_DRV_PRIV ||
+ rq->cmd_type == REQ_TYPE_ATA_SENSE));
/* Retry a failed packet command */
if (drive->failed_pc && drive->pc->c[0] == REQUEST_SENSE) {
@@ -853,7 +853,7 @@ static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int size)
BUG_ON(size < 0 || size % tape->blk_size);
rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
rq->cmd[13] = cmd;
rq->rq_disk = tape->disk;
rq->__sector = tape->first_frame;
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index dabb88b..0979e12 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -186,7 +186,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
tf->command == ATA_CMD_CHK_POWER) {
struct request *rq = hwif->rq;
- if (blk_pm_request(rq))
+ if (ata_pm_request(rq))
ide_complete_pm_rq(drive, rq);
else
ide_finish_cmd(drive, cmd, stat);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7afa6a2..d20fe1d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -980,8 +980,7 @@ config TOUCHSCREEN_SUN4I
config TOUCHSCREEN_SUR40
tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
- depends on USB
- depends on MEDIA_USB_SUPPORT
+ depends on USB && MEDIA_USB_SUPPORT && HAS_DMA
select INPUT_POLLDEV
select VIDEOBUF2_DMA_SG
help
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index a24eba5..8be7b9b 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -125,7 +125,7 @@ struct sur40_image_header {
#define VIDEO_PACKET_SIZE 16384
/* polling interval (ms) */
-#define POLL_INTERVAL 10
+#define POLL_INTERVAL 4
/* maximum number of contacts FIXME: this is a guess? */
#define MAX_CONTACTS 64
@@ -342,7 +342,7 @@ static void sur40_poll(struct input_polled_dev *polldev)
* instead of at the end.
*/
if (packet_id != header->packet_id)
- dev_warn(sur40->dev, "packet ID mismatch\n");
+ dev_dbg(sur40->dev, "packet ID mismatch\n");
packet_blobs = result / sizeof(struct sur40_blob);
dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
@@ -389,6 +389,8 @@ static void sur40_process_video(struct sur40_state *sur40)
list_del(&new_buf->list);
spin_unlock(&sur40->qlock);
+ dev_dbg(sur40->dev, "buffer acquired\n");
+
/* retrieve data via bulk read */
result = usb_bulk_msg(sur40->usbdev,
usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT),
@@ -416,6 +418,8 @@ static void sur40_process_video(struct sur40_state *sur40)
goto err_poll;
}
+ dev_dbg(sur40->dev, "header acquired\n");
+
sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0);
result = usb_sg_init(&sgr, sur40->usbdev,
@@ -432,11 +436,18 @@ static void sur40_process_video(struct sur40_state *sur40)
goto err_poll;
}
+ dev_dbg(sur40->dev, "image acquired\n");
+
+ /* return error if streaming was stopped in the meantime */
+ if (sur40->sequence == -1)
+ goto err_poll;
+
/* mark as finished */
v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
new_buf->vb.v4l2_buf.sequence = sur40->sequence++;
new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
+ dev_dbg(sur40->dev, "buffer marked done\n");
return;
err_poll:
@@ -716,6 +727,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
static void sur40_stop_streaming(struct vb2_queue *vq)
{
struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+ sur40->sequence = -1;
/* Release all active buffers */
return_all_buffers(sur40, VB2_BUF_STATE_ERROR);
@@ -778,6 +790,33 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
return 0;
}
+static int sur40_vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *f)
+{
+ if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY))
+ return -EINVAL;
+
+ f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ f->discrete.width = sur40_video_format.width;
+ f->discrete.height = sur40_video_format.height;
+ return 0;
+}
+
+static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *f)
+{
+ if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY)
+ || (f->width != sur40_video_format.width)
+ || (f->height != sur40_video_format.height))
+ return -EINVAL;
+
+ f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ f->discrete.denominator = 60/(f->index+1);
+ f->discrete.numerator = 1;
+ return 0;
+}
+
+
static const struct usb_device_id sur40_table[] = {
{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */
{ } /* terminating null entry */
@@ -829,6 +868,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
.vidioc_s_fmt_vid_cap = sur40_vidioc_fmt,
.vidioc_g_fmt_vid_cap = sur40_vidioc_fmt,
+ .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals,
+
.vidioc_enum_input = sur40_vidioc_enum_input,
.vidioc_g_input = sur40_vidioc_g_input,
.vidioc_s_input = sur40_vidioc_s_input,
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 99b9a97..8a7d780 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -159,3 +159,11 @@ config KEYSTONE_IRQ
config MIPS_GIC
bool
select MIPS_CM
+
+config RENESAS_H8300H_INTC
+ bool
+ select IRQ_DOMAIN
+
+config RENESAS_H8S_INTC
+ bool
+ select IRQ_DOMAIN \ No newline at end of file
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index dda4927..f8efb70 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -47,3 +47,5 @@ obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
+obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
+obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
diff --git a/drivers/irqchip/irq-renesas-h8300h.c b/drivers/irqchip/irq-renesas-h8300h.c
new file mode 100644
index 0000000..1870e6b
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-h8300h.c
@@ -0,0 +1,95 @@
+/*
+ * H8/300H interrupt controller driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+#include "irqchip.h"
+
+static const char ipr_bit[] = {
+ 7, 6, 5, 5,
+ 4, 4, 4, 4, 3, 3, 3, 3,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 0, 0, 0, 15, 15, 15, 15,
+ 14, 14, 14, 14, 13, 13, 13, 13,
+ -1, -1, -1, -1, 11, 11, 11, 11,
+ 10, 10, 10, 10, 9, 9, 9, 9,
+};
+
+static void *intc_baseaddr;
+
+#define IPR ((unsigned long)intc_baseaddr + 6)
+
+static void h8300h_disable_irq(struct irq_data *data)
+{
+ int bit;
+ int irq = data->irq - 12;
+
+ bit = ipr_bit[irq];
+ if (bit >= 0) {
+ if (bit < 8)
+ ctrl_bclr(bit & 7, IPR);
+ else
+ ctrl_bclr(bit & 7, (IPR+1));
+ }
+}
+
+static void h8300h_enable_irq(struct irq_data *data)
+{
+ int bit;
+ int irq = data->irq - 12;
+
+ bit = ipr_bit[irq];
+ if (bit >= 0) {
+ if (bit < 8)
+ ctrl_bset(bit & 7, IPR);
+ else
+ ctrl_bset(bit & 7, (IPR+1));
+ }
+}
+
+struct irq_chip h8300h_irq_chip = {
+ .name = "H8/300H-INTC",
+ .irq_enable = h8300h_enable_irq,
+ .irq_disable = h8300h_disable_irq,
+};
+
+static int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &h8300h_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init h8300h_intc_of_init(struct device_node *intc,
+ struct device_node *parent)
+{
+ struct irq_domain *domain;
+
+ intc_baseaddr = of_iomap(intc, 0);
+ BUG_ON(!intc_baseaddr);
+
+ /* All interrupt priority low */
+ ctrl_outb(0x00, IPR + 0);
+ ctrl_outb(0x00, IPR + 1);
+
+ domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
+ BUG_ON(!domain);
+ irq_set_default_host(domain);
+ return 0;
+}
+
+IRQCHIP_DECLARE(h8300h_intc, "renesas,h8300h-intc", h8300h_intc_of_init);
diff --git a/drivers/irqchip/irq-renesas-h8s.c b/drivers/irqchip/irq-renesas-h8s.c
new file mode 100644
index 0000000..64425f4
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-h8s.c
@@ -0,0 +1,101 @@
+/*
+ * H8S interrupt contoller driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+#include "irqchip.h"
+
+static void *intc_baseaddr;
+#define IPRA ((unsigned long)intc_baseaddr)
+
+static const unsigned char ipr_table[] = {
+ 0x03, 0x02, 0x01, 0x00, 0x13, 0x12, 0x11, 0x10, /* 16 - 23 */
+ 0x23, 0x22, 0x21, 0x20, 0x33, 0x32, 0x31, 0x30, /* 24 - 31 */
+ 0x43, 0x42, 0x41, 0x40, 0x53, 0x53, 0x52, 0x52, /* 32 - 39 */
+ 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, /* 40 - 47 */
+ 0x50, 0x50, 0x50, 0x50, 0x63, 0x63, 0x63, 0x63, /* 48 - 55 */
+ 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, /* 56 - 63 */
+ 0x61, 0x61, 0x61, 0x61, 0x60, 0x60, 0x60, 0x60, /* 64 - 71 */
+ 0x73, 0x73, 0x73, 0x73, 0x72, 0x72, 0x72, 0x72, /* 72 - 79 */
+ 0x71, 0x71, 0x71, 0x71, 0x70, 0x83, 0x82, 0x81, /* 80 - 87 */
+ 0x80, 0x80, 0x80, 0x80, 0x93, 0x93, 0x93, 0x93, /* 88 - 95 */
+ 0x92, 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, /* 96 - 103 */
+ 0x90, 0x90, 0x90, 0x90, 0xa3, 0xa3, 0xa3, 0xa3, /* 104 - 111 */
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, /* 112 - 119 */
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, /* 120 - 127 */
+};
+
+static void h8s_disable_irq(struct irq_data *data)
+{
+ int pos;
+ unsigned int addr;
+ unsigned short pri;
+ int irq = data->irq;
+
+ addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3);
+ pos = (ipr_table[irq - 16] & 0x0f) * 4;
+ pri = ~(0x000f << pos);
+ pri &= ctrl_inw(addr);
+ ctrl_outw(pri, addr);
+}
+
+static void h8s_enable_irq(struct irq_data *data)
+{
+ int pos;
+ unsigned int addr;
+ unsigned short pri;
+ int irq = data->irq;
+
+ addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3);
+ pos = (ipr_table[irq - 16] & 0x0f) * 4;
+ pri = ~(0x000f << pos);
+ pri &= ctrl_inw(addr);
+ pri |= 1 << pos;
+ ctrl_outw(pri, addr);
+}
+
+struct irq_chip h8s_irq_chip = {
+ .name = "H8S-INTC",
+ .irq_enable = h8s_enable_irq,
+ .irq_disable = h8s_disable_irq,
+};
+
+static __init int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &h8s_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init h8s_intc_of_init(struct device_node *intc,
+ struct device_node *parent)
+{
+ struct irq_domain *domain;
+ int n;
+
+ intc_baseaddr = of_iomap(intc, 0);
+ BUG_ON(!intc_baseaddr);
+
+ /* All interrupt priority is 0 (disable) */
+ /* IPRA to IPRK */
+ for (n = 0; n <= 'k' - 'a'; n++)
+ ctrl_outw(0x0000, IPRA + (n * 2));
+
+ domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
+ BUG_ON(!domain);
+ irq_set_default_host(domain);
+ return 0;
+}
+
+IRQCHIP_DECLARE(h8s_intc, "renesas,h8s-intc", h8s_intc_of_init);
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 84b0a2d..e269f08 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -56,8 +56,18 @@ config PCC
config ALTERA_MBOX
tristate "Altera Mailbox"
+ depends on HAS_IOMEM
help
An implementation of the Altera Mailbox soft core. It is used
to send message between processors. Say Y here if you want to use the
Altera mailbox support.
+
+config BCM2835_MBOX
+ tristate "BCM2835 Mailbox"
+ depends on ARCH_BCM2835
+ help
+ An implementation of the BCM2385 Mailbox. It is used to invoke
+ the services of the Videocore. Say Y here if you want to use the
+ BCM2835 Mailbox.
+
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index b18201e..8e6d822 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
obj-$(CONFIG_PCC) += pcc.o
obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o
+
+obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
index ac693c6..d9e99f9 100644
--- a/drivers/mailbox/arm_mhu.c
+++ b/drivers/mailbox/arm_mhu.c
@@ -110,7 +110,7 @@ static void mhu_shutdown(struct mbox_chan *chan)
free_irq(mlink->irq, chan);
}
-static struct mbox_chan_ops mhu_ops = {
+static const struct mbox_chan_ops mhu_ops = {
.send_data = mhu_send_data,
.startup = mhu_startup,
.shutdown = mhu_shutdown,
diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c
new file mode 100644
index 0000000..0b47dd4
--- /dev/null
+++ b/drivers/mailbox/bcm2835-mailbox.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010,2015 Broadcom
+ * Copyright (C) 2013-2014 Lubomir Rintel
+ * Copyright (C) 2013 Craig McGeachie
+ *
+ * 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 device provides a mechanism for writing to the mailboxes,
+ * that are shared between the ARM and the VideoCore processor
+ *
+ * Parts of the driver are based on:
+ * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was
+ * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/
+ * linux.git
+ * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at
+ * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/
+ * mailbox/bcm2835-ipc.c
+ * - documentation available on the following web site:
+ * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* Mailboxes */
+#define ARM_0_MAIL0 0x00
+#define ARM_0_MAIL1 0x20
+
+/*
+ * Mailbox registers. We basically only support mailbox 0 & 1. We
+ * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
+ * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
+ * the placement of memory barriers.
+ */
+#define MAIL0_RD (ARM_0_MAIL0 + 0x00)
+#define MAIL0_POL (ARM_0_MAIL0 + 0x10)
+#define MAIL0_STA (ARM_0_MAIL0 + 0x18)
+#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C)
+#define MAIL1_WRT (ARM_0_MAIL1 + 0x00)
+#define MAIL1_STA (ARM_0_MAIL1 + 0x18)
+
+/* Status register: FIFO state. */
+#define ARM_MS_FULL BIT(31)
+#define ARM_MS_EMPTY BIT(30)
+
+/* Configuration register: Enable interrupts. */
+#define ARM_MC_IHAVEDATAIRQEN BIT(0)
+
+struct bcm2835_mbox {
+ void __iomem *regs;
+ spinlock_t lock;
+ struct mbox_controller controller;
+};
+
+static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link)
+{
+ return container_of(link->mbox, struct bcm2835_mbox, controller);
+}
+
+static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id)
+{
+ struct bcm2835_mbox *mbox = dev_id;
+ struct device *dev = mbox->controller.dev;
+ struct mbox_chan *link = &mbox->controller.chans[0];
+
+ while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) {
+ u32 msg = readl(mbox->regs + MAIL0_RD);
+ dev_dbg(dev, "Reply 0x%08X\n", msg);
+ mbox_chan_received_data(link, &msg);
+ }
+ return IRQ_HANDLED;
+}
+
+static int bcm2835_send_data(struct mbox_chan *link, void *data)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+ u32 msg = *(u32 *)data;
+
+ spin_lock(&mbox->lock);
+ writel(msg, mbox->regs + MAIL1_WRT);
+ dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg);
+ spin_unlock(&mbox->lock);
+ return 0;
+}
+
+static int bcm2835_startup(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+
+ /* Enable the interrupt on data reception */
+ writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF);
+
+ return 0;
+}
+
+static void bcm2835_shutdown(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+
+ writel(0, mbox->regs + MAIL0_CNF);
+}
+
+static bool bcm2835_last_tx_done(struct mbox_chan *link)
+{
+ struct bcm2835_mbox *mbox = bcm2835_link_mbox(link);
+ bool ret;
+
+ spin_lock(&mbox->lock);
+ ret = !(readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL);
+ spin_unlock(&mbox->lock);
+ return ret;
+}
+
+static const struct mbox_chan_ops bcm2835_mbox_chan_ops = {
+ .send_data = bcm2835_send_data,
+ .startup = bcm2835_startup,
+ .shutdown = bcm2835_shutdown,
+ .last_tx_done = bcm2835_last_tx_done
+};
+
+static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *sp)
+{
+ if (sp->args_count != 0)
+ return NULL;
+
+ return &mbox->chans[0];
+}
+
+static int bcm2835_mbox_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+ struct resource *iomem;
+ struct bcm2835_mbox *mbox;
+
+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ if (mbox == NULL)
+ return -ENOMEM;
+ spin_lock_init(&mbox->lock);
+
+ ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
+ bcm2835_mbox_irq, 0, dev_name(dev), mbox);
+ if (ret) {
+ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(mbox->regs)) {
+ ret = PTR_ERR(mbox->regs);
+ dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret);
+ return ret;
+ }
+
+ mbox->controller.txdone_poll = true;
+ mbox->controller.txpoll_period = 5;
+ mbox->controller.ops = &bcm2835_mbox_chan_ops;
+ mbox->controller.of_xlate = &bcm2835_mbox_index_xlate;
+ mbox->controller.dev = dev;
+ mbox->controller.num_chans = 1;
+ mbox->controller.chans = devm_kzalloc(dev,
+ sizeof(*mbox->controller.chans), GFP_KERNEL);
+ if (!mbox->controller.chans)
+ return -ENOMEM;
+
+ ret = mbox_controller_register(&mbox->controller);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, mbox);
+ dev_info(dev, "mailbox enabled\n");
+
+ return ret;
+}
+
+static int bcm2835_mbox_remove(struct platform_device *pdev)
+{
+ struct bcm2835_mbox *mbox = platform_get_drvdata(pdev);
+ mbox_controller_unregister(&mbox->controller);
+ return 0;
+}
+
+static const struct of_device_id bcm2835_mbox_of_match[] = {
+ { .compatible = "brcm,bcm2835-mbox", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match);
+
+static struct platform_driver bcm2835_mbox_driver = {
+ .driver = {
+ .name = "bcm2835-mbox",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_mbox_of_match,
+ },
+ .probe = bcm2835_mbox_probe,
+ .remove = bcm2835_mbox_remove,
+};
+module_platform_driver(bcm2835_mbox_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("BCM2835 mailbox IPC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c
index a266265..bb682c9 100644
--- a/drivers/mailbox/mailbox-altera.c
+++ b/drivers/mailbox/mailbox-altera.c
@@ -285,7 +285,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan)
}
}
-static struct mbox_chan_ops altera_mbox_ops = {
+static const struct mbox_chan_ops altera_mbox_ops = {
.send_data = altera_mbox_send_data,
.startup = altera_mbox_startup,
.shutdown = altera_mbox_shutdown,
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 19b491d..c7fdb57 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -318,7 +318,7 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
return ERR_PTR(-ENODEV);
}
- chan = NULL;
+ chan = ERR_PTR(-EPROBE_DEFER);
list_for_each_entry(mbox, &mbox_cons, node)
if (mbox->dev->of_node == spec.np) {
chan = mbox->of_xlate(mbox, &spec);
@@ -327,7 +327,12 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
of_node_put(spec.np);
- if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) {
+ if (IS_ERR(chan)) {
+ mutex_unlock(&con_mutex);
+ return chan;
+ }
+
+ if (chan->cl || !try_module_get(mbox->dev->driver->owner)) {
dev_dbg(dev, "%s: mailbox not free\n", __func__);
mutex_unlock(&con_mutex);
return ERR_PTR(-EBUSY);
@@ -357,6 +362,35 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
}
EXPORT_SYMBOL_GPL(mbox_request_channel);
+struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
+ const char *name)
+{
+ struct device_node *np = cl->dev->of_node;
+ struct property *prop;
+ const char *mbox_name;
+ int index = 0;
+
+ if (!np) {
+ dev_err(cl->dev, "%s() currently only supports DT\n", __func__);
+ return ERR_PTR(-ENOSYS);
+ }
+
+ if (!of_get_property(np, "mbox-names", NULL)) {
+ dev_err(cl->dev,
+ "%s() requires an \"mbox-names\" property\n", __func__);
+ return ERR_PTR(-ENOSYS);
+ }
+
+ of_property_for_each_string(np, "mbox-names", prop, mbox_name) {
+ if (!strncmp(name, mbox_name, strlen(name)))
+ break;
+ index++;
+ }
+
+ return mbox_request_channel(cl, index);
+}
+EXPORT_SYMBOL_GPL(mbox_request_channel_byname);
+
/**
* mbox_free_channel - The client relinquishes control of a mailbox
* channel by this call.
@@ -390,7 +424,7 @@ of_mbox_index_xlate(struct mbox_controller *mbox,
int ind = sp->args[0];
if (ind >= mbox->num_chans)
- return NULL;
+ return ERR_PTR(-EINVAL);
return &mbox->chans[ind];
}
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
index 0f332c1..a3dbfd9 100644
--- a/drivers/mailbox/omap-mailbox.c
+++ b/drivers/mailbox/omap-mailbox.c
@@ -604,7 +604,7 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data)
return ret;
}
-static struct mbox_chan_ops omap_mbox_chan_ops = {
+static const struct mbox_chan_ops omap_mbox_chan_ops = {
.startup = omap_mbox_chan_startup,
.send_data = omap_mbox_chan_send_data,
.shutdown = omap_mbox_chan_shutdown,
@@ -639,18 +639,18 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller,
mdev = container_of(controller, struct omap_mbox_device, controller);
if (WARN_ON(!mdev))
- return NULL;
+ return ERR_PTR(-EINVAL);
node = of_find_node_by_phandle(phandle);
if (!node) {
pr_err("%s: could not find node phandle 0x%x\n",
__func__, phandle);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
mbox = omap_mbox_device_find(mdev, node->name);
of_node_put(node);
- return mbox ? mbox->chan : NULL;
+ return mbox ? mbox->chan : ERR_PTR(-ENOENT);
}
static int omap_mbox_probe(struct platform_device *pdev)
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 7e91d68..26d121d 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -198,7 +198,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
return 0;
}
-static struct mbox_chan_ops pcc_chan_ops = {
+static const struct mbox_chan_ops pcc_chan_ops = {
.send_data = pcc_send_data,
};
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index edcf4ab..b597273 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -304,6 +304,18 @@ config DM_CACHE_MQ
This is meant to be a general purpose policy. It prioritises
reads over writes.
+config DM_CACHE_SMQ
+ tristate "Stochastic MQ Cache Policy (EXPERIMENTAL)"
+ depends on DM_CACHE
+ default y
+ ---help---
+ A cache policy that uses a multiqueue ordered by recent hits
+ to select which blocks should be promoted and demoted.
+ This is meant to be a general purpose policy. It prioritises
+ reads over writes. This SMQ policy (vs MQ) offers the promise
+ of less memory utilization, improved performance and increased
+ adaptability in the face of changing workloads.
+
config DM_CACHE_CLEANER
tristate "Cleaner Cache Policy (EXPERIMENTAL)"
depends on DM_CACHE
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index dba4db5..462f443 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -13,6 +13,7 @@ dm-log-userspace-y \
dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o
dm-cache-mq-y += dm-cache-policy-mq.o
+dm-cache-smq-y += dm-cache-policy-smq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
dm-era-y += dm-era-target.o
md-mod-y += md.o bitmap.o
@@ -54,6 +55,7 @@ obj-$(CONFIG_DM_THIN_PROVISIONING) += dm-thin-pool.o
obj-$(CONFIG_DM_VERITY) += dm-verity.o
obj-$(CONFIG_DM_CACHE) += dm-cache.o
obj-$(CONFIG_DM_CACHE_MQ) += dm-cache-mq.o
+obj-$(CONFIG_DM_CACHE_SMQ) += dm-cache-smq.o
obj-$(CONFIG_DM_CACHE_CLEANER) += dm-cache-cleaner.o
obj-$(CONFIG_DM_ERA) += dm-era.o
obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
diff --git a/drivers/md/bcache/io.c b/drivers/md/bcache/io.c
index fa028fa..cb64e64a 100644
--- a/drivers/md/bcache/io.c
+++ b/drivers/md/bcache/io.c
@@ -55,7 +55,7 @@ static void bch_bio_submit_split_done(struct closure *cl)
s->bio->bi_end_io = s->bi_end_io;
s->bio->bi_private = s->bi_private;
- bio_endio_nodec(s->bio, 0);
+ bio_endio(s->bio, 0);
closure_debug_destroy(&s->cl);
mempool_free(s, s->p->bio_split_hook);
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index ab43fad..4afb2d2 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/hash.h>
#include <linux/random.h>
+#include <linux/backing-dev.h>
#include <trace/events/bcache.h>
@@ -619,7 +620,7 @@ static void do_bio_hook(struct search *s, struct bio *orig_bio)
bio->bi_end_io = request_endio;
bio->bi_private = &s->cl;
- atomic_set(&bio->bi_cnt, 3);
+ bio_cnt_set(bio, 3);
}
static void search_free(struct closure *cl)
diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c
index be06530..cd6d1d2 100644
--- a/drivers/md/dm-bio-prison.c
+++ b/drivers/md/dm-bio-prison.c
@@ -255,6 +255,32 @@ void dm_cell_visit_release(struct dm_bio_prison *prison,
}
EXPORT_SYMBOL_GPL(dm_cell_visit_release);
+static int __promote_or_release(struct dm_bio_prison *prison,
+ struct dm_bio_prison_cell *cell)
+{
+ if (bio_list_empty(&cell->bios)) {
+ rb_erase(&cell->node, &prison->cells);
+ return 1;
+ }
+
+ cell->holder = bio_list_pop(&cell->bios);
+ return 0;
+}
+
+int dm_cell_promote_or_release(struct dm_bio_prison *prison,
+ struct dm_bio_prison_cell *cell)
+{
+ int r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prison->lock, flags);
+ r = __promote_or_release(prison, cell);
+ spin_unlock_irqrestore(&prison->lock, flags);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_cell_promote_or_release);
+
/*----------------------------------------------------------------*/
#define DEFERRED_SET_SIZE 64
diff --git a/drivers/md/dm-bio-prison.h b/drivers/md/dm-bio-prison.h
index 74cf011..54352f0 100644
--- a/drivers/md/dm-bio-prison.h
+++ b/drivers/md/dm-bio-prison.h
@@ -101,6 +101,19 @@ void dm_cell_visit_release(struct dm_bio_prison *prison,
void (*visit_fn)(void *, struct dm_bio_prison_cell *),
void *context, struct dm_bio_prison_cell *cell);
+/*
+ * Rather than always releasing the prisoners in a cell, the client may
+ * want to promote one of them to be the new holder. There is a race here
+ * though between releasing an empty cell, and other threads adding new
+ * inmates. So this function makes the decision with its lock held.
+ *
+ * This function can have two outcomes:
+ * i) An inmate is promoted to be the holder of the cell (return value of 0).
+ * ii) The cell has no inmate for promotion and is released (return value of 1).
+ */
+int dm_cell_promote_or_release(struct dm_bio_prison *prison,
+ struct dm_bio_prison_cell *cell);
+
/*----------------------------------------------------------------*/
/*
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index c1c0104..20cc36b 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -39,6 +39,8 @@
enum superblock_flag_bits {
/* for spotting crashes that would invalidate the dirty bitset */
CLEAN_SHUTDOWN,
+ /* metadata must be checked using the tools */
+ NEEDS_CHECK,
};
/*
@@ -107,6 +109,7 @@ struct dm_cache_metadata {
struct dm_disk_bitset discard_info;
struct rw_semaphore root_lock;
+ unsigned long flags;
dm_block_t root;
dm_block_t hint_root;
dm_block_t discard_root;
@@ -129,6 +132,14 @@ struct dm_cache_metadata {
* buffer before the superblock is locked and updated.
*/
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
+
+ /*
+ * Set if a transaction has to be aborted but the attempt to roll
+ * back to the previous (good) transaction failed. The only
+ * metadata operation permissible in this state is the closing of
+ * the device.
+ */
+ bool fail_io:1;
};
/*-------------------------------------------------------------------
@@ -527,6 +538,7 @@ static unsigned long clear_clean_shutdown(unsigned long flags)
static void read_superblock_fields(struct dm_cache_metadata *cmd,
struct cache_disk_superblock *disk_super)
{
+ cmd->flags = le32_to_cpu(disk_super->flags);
cmd->root = le64_to_cpu(disk_super->mapping_root);
cmd->hint_root = le64_to_cpu(disk_super->hint_root);
cmd->discard_root = le64_to_cpu(disk_super->discard_root);
@@ -625,6 +637,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
if (mutator)
update_flags(disk_super, mutator);
+ disk_super->flags = cpu_to_le32(cmd->flags);
disk_super->mapping_root = cpu_to_le64(cmd->root);
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
@@ -693,6 +706,7 @@ static struct dm_cache_metadata *metadata_open(struct block_device *bdev,
cmd->cache_blocks = 0;
cmd->policy_hint_size = policy_hint_size;
cmd->changed = true;
+ cmd->fail_io = false;
r = __create_persistent_data_objects(cmd, may_format_device);
if (r) {
@@ -796,7 +810,8 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd)
list_del(&cmd->list);
mutex_unlock(&table_lock);
- __destroy_persistent_data_objects(cmd);
+ if (!cmd->fail_io)
+ __destroy_persistent_data_objects(cmd);
kfree(cmd);
}
}
@@ -848,13 +863,26 @@ static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd,
return 0;
}
+#define WRITE_LOCK(cmd) \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+ return -EINVAL; \
+ down_write(&cmd->root_lock)
+
+#define WRITE_LOCK_VOID(cmd) \
+ if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+ return; \
+ down_write(&cmd->root_lock)
+
+#define WRITE_UNLOCK(cmd) \
+ up_write(&cmd->root_lock)
+
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
{
int r;
bool clean;
__le64 null_mapping = pack_value(0, 0);
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
__dm_bless_for_disk(&null_mapping);
if (from_cblock(new_cache_size) < from_cblock(cmd->cache_blocks)) {
@@ -880,7 +908,7 @@ int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
cmd->changed = true;
out:
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -891,7 +919,7 @@ int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = dm_bitset_resize(&cmd->discard_info,
cmd->discard_root,
from_dblock(cmd->discard_nr_blocks),
@@ -903,7 +931,7 @@ int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
}
cmd->changed = true;
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -946,9 +974,9 @@ int dm_cache_set_discard(struct dm_cache_metadata *cmd,
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = __discard(cmd, dblock, discard);
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1020,9 +1048,9 @@ int dm_cache_remove_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock)
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = __remove(cmd, cblock);
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1048,9 +1076,9 @@ int dm_cache_insert_mapping(struct dm_cache_metadata *cmd,
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = __insert(cmd, cblock, oblock);
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1234,9 +1262,9 @@ int dm_cache_set_dirty(struct dm_cache_metadata *cmd,
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = __dirty(cmd, cblock, dirty);
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1252,9 +1280,9 @@ void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats)
{
- down_write(&cmd->root_lock);
+ WRITE_LOCK_VOID(cmd);
cmd->stats = *stats;
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
}
int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown)
@@ -1263,7 +1291,7 @@ int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown)
flags_mutator mutator = (clean_shutdown ? set_clean_shutdown :
clear_clean_shutdown);
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = __commit_transaction(cmd, mutator);
if (r)
goto out;
@@ -1271,7 +1299,7 @@ int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown)
r = __begin_transaction(cmd);
out:
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1376,9 +1404,9 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *
{
int r;
- down_write(&cmd->root_lock);
+ WRITE_LOCK(cmd);
r = write_hints(cmd, policy);
- up_write(&cmd->root_lock);
+ WRITE_UNLOCK(cmd);
return r;
}
@@ -1387,3 +1415,70 @@ int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result)
{
return blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
}
+
+void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd)
+{
+ WRITE_LOCK_VOID(cmd);
+ dm_bm_set_read_only(cmd->bm);
+ WRITE_UNLOCK(cmd);
+}
+
+void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd)
+{
+ WRITE_LOCK_VOID(cmd);
+ dm_bm_set_read_write(cmd->bm);
+ WRITE_UNLOCK(cmd);
+}
+
+int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
+{
+ int r;
+ struct dm_block *sblock;
+ struct cache_disk_superblock *disk_super;
+
+ /*
+ * We ignore fail_io for this function.
+ */
+ down_write(&cmd->root_lock);
+ set_bit(NEEDS_CHECK, &cmd->flags);
+
+ r = superblock_lock(cmd, &sblock);
+ if (r) {
+ DMERR("couldn't read superblock");
+ goto out;
+ }
+
+ disk_super = dm_block_data(sblock);
+ disk_super->flags = cpu_to_le32(cmd->flags);
+
+ dm_bm_unlock(sblock);
+
+out:
+ up_write(&cmd->root_lock);
+ return r;
+}
+
+bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd)
+{
+ bool needs_check;
+
+ down_read(&cmd->root_lock);
+ needs_check = !!test_bit(NEEDS_CHECK, &cmd->flags);
+ up_read(&cmd->root_lock);
+
+ return needs_check;
+}
+
+int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
+{
+ int r;
+
+ WRITE_LOCK(cmd);
+ __destroy_persistent_data_objects(cmd);
+ r = __create_persistent_data_objects(cmd, false);
+ if (r)
+ cmd->fail_io = true;
+ WRITE_UNLOCK(cmd);
+
+ return r;
+}
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h
index 4ecc403..2ffee21 100644
--- a/drivers/md/dm-cache-metadata.h
+++ b/drivers/md/dm-cache-metadata.h
@@ -102,6 +102,10 @@ struct dm_cache_statistics {
void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats);
+
+/*
+ * 'void' because it's no big deal if it fails.
+ */
void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
struct dm_cache_statistics *stats);
@@ -133,6 +137,12 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *
*/
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result);
+bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd);
+int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd);
+void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd);
+void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd);
+int dm_cache_metadata_abort(struct dm_cache_metadata *cmd);
+
/*----------------------------------------------------------------*/
#endif /* DM_CACHE_METADATA_H */
diff --git a/drivers/md/dm-cache-policy-cleaner.c b/drivers/md/dm-cache-policy-cleaner.c
index b04d1f9..240c9f0 100644
--- a/drivers/md/dm-cache-policy-cleaner.c
+++ b/drivers/md/dm-cache-policy-cleaner.c
@@ -171,7 +171,8 @@ static void remove_cache_hash_entry(struct wb_cache_entry *e)
/* Public interface (see dm-cache-policy.h */
static int wb_map(struct dm_cache_policy *pe, dm_oblock_t oblock,
bool can_block, bool can_migrate, bool discarded_oblock,
- struct bio *bio, struct policy_result *result)
+ struct bio *bio, struct policy_locker *locker,
+ struct policy_result *result)
{
struct policy *p = to_policy(pe);
struct wb_cache_entry *e;
@@ -358,7 +359,8 @@ static struct wb_cache_entry *get_next_dirty_entry(struct policy *p)
static int wb_writeback_work(struct dm_cache_policy *pe,
dm_oblock_t *oblock,
- dm_cblock_t *cblock)
+ dm_cblock_t *cblock,
+ bool critical_only)
{
int r = -ENOENT;
struct policy *p = to_policy(pe);
diff --git a/drivers/md/dm-cache-policy-internal.h b/drivers/md/dm-cache-policy-internal.h
index 2256a1f..2816018 100644
--- a/drivers/md/dm-cache-policy-internal.h
+++ b/drivers/md/dm-cache-policy-internal.h
@@ -7,6 +7,7 @@
#ifndef DM_CACHE_POLICY_INTERNAL_H
#define DM_CACHE_POLICY_INTERNAL_H
+#include <linux/vmalloc.h>
#include "dm-cache-policy.h"
/*----------------------------------------------------------------*/
@@ -16,9 +17,10 @@
*/
static inline int policy_map(struct dm_cache_policy *p, dm_oblock_t oblock,
bool can_block, bool can_migrate, bool discarded_oblock,
- struct bio *bio, struct policy_result *result)
+ struct bio *bio, struct policy_locker *locker,
+ struct policy_result *result)
{
- return p->map(p, oblock, can_block, can_migrate, discarded_oblock, bio, result);
+ return p->map(p, oblock, can_block, can_migrate, discarded_oblock, bio, locker, result);
}
static inline int policy_lookup(struct dm_cache_policy *p, dm_oblock_t oblock, dm_cblock_t *cblock)
@@ -54,9 +56,10 @@ static inline int policy_walk_mappings(struct dm_cache_policy *p,
static inline int policy_writeback_work(struct dm_cache_policy *p,
dm_oblock_t *oblock,
- dm_cblock_t *cblock)
+ dm_cblock_t *cblock,
+ bool critical_only)
{
- return p->writeback_work ? p->writeback_work(p, oblock, cblock) : -ENOENT;
+ return p->writeback_work ? p->writeback_work(p, oblock, cblock, critical_only) : -ENOENT;
}
static inline void policy_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
@@ -80,19 +83,21 @@ static inline dm_cblock_t policy_residency(struct dm_cache_policy *p)
return p->residency(p);
}
-static inline void policy_tick(struct dm_cache_policy *p)
+static inline void policy_tick(struct dm_cache_policy *p, bool can_block)
{
if (p->tick)
- return p->tick(p);
+ return p->tick(p, can_block);
}
-static inline int policy_emit_config_values(struct dm_cache_policy *p, char *result, unsigned maxlen)
+static inline int policy_emit_config_values(struct dm_cache_policy *p, char *result,
+ unsigned maxlen, ssize_t *sz_ptr)
{
- ssize_t sz = 0;
+ ssize_t sz = *sz_ptr;
if (p->emit_config_values)
- return p->emit_config_values(p, result, maxlen);
+ return p->emit_config_values(p, result, maxlen, sz_ptr);
- DMEMIT("0");
+ DMEMIT("0 ");
+ *sz_ptr = sz;
return 0;
}
@@ -105,6 +110,33 @@ static inline int policy_set_config_value(struct dm_cache_policy *p,
/*----------------------------------------------------------------*/
/*
+ * Some utility functions commonly used by policies and the core target.
+ */
+static inline size_t bitset_size_in_bytes(unsigned nr_entries)
+{
+ return sizeof(unsigned long) * dm_div_up(nr_entries, BITS_PER_LONG);
+}
+
+static inline unsigned long *alloc_bitset(unsigned nr_entries)
+{
+ size_t s = bitset_size_in_bytes(nr_entries);
+ return vzalloc(s);
+}
+
+static inline void clear_bitset(void *bitset, unsigned nr_entries)
+{
+ size_t s = bitset_size_in_bytes(nr_entries);
+ memset(bitset, 0, s);
+}
+
+static inline void free_bitset(unsigned long *bits)
+{
+ vfree(bits);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
* Creates a new cache policy given a policy name, a cache size, an origin size and the block size.
*/
struct dm_cache_policy *dm_cache_policy_create(const char *name, dm_cblock_t cache_size,
diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c
index 3ddd116..3281437 100644
--- a/drivers/md/dm-cache-policy-mq.c
+++ b/drivers/md/dm-cache-policy-mq.c
@@ -693,9 +693,10 @@ static void requeue(struct mq_policy *mq, struct entry *e)
* - set the hit count to a hard coded value other than 1, eg, is it better
* if it goes in at level 2?
*/
-static int demote_cblock(struct mq_policy *mq, dm_oblock_t *oblock)
+static int demote_cblock(struct mq_policy *mq,
+ struct policy_locker *locker, dm_oblock_t *oblock)
{
- struct entry *demoted = pop(mq, &mq->cache_clean);
+ struct entry *demoted = peek(&mq->cache_clean);
if (!demoted)
/*
@@ -707,6 +708,13 @@ static int demote_cblock(struct mq_policy *mq, dm_oblock_t *oblock)
*/
return -ENOSPC;
+ if (locker->fn(locker, demoted->oblock))
+ /*
+ * We couldn't lock the demoted block.
+ */
+ return -EBUSY;
+
+ del(mq, demoted);
*oblock = demoted->oblock;
free_entry(&mq->cache_pool, demoted);
@@ -795,6 +803,7 @@ static int cache_entry_found(struct mq_policy *mq,
* finding which cache block to use.
*/
static int pre_cache_to_cache(struct mq_policy *mq, struct entry *e,
+ struct policy_locker *locker,
struct policy_result *result)
{
int r;
@@ -803,11 +812,12 @@ static int pre_cache_to_cache(struct mq_policy *mq, struct entry *e,
/* Ensure there's a free cblock in the cache */
if (epool_empty(&mq->cache_pool)) {
result->op = POLICY_REPLACE;
- r = demote_cblock(mq, &result->old_oblock);
+ r = demote_cblock(mq, locker, &result->old_oblock);
if (r) {
result->op = POLICY_MISS;
return 0;
}
+
} else
result->op = POLICY_NEW;
@@ -829,7 +839,8 @@ static int pre_cache_to_cache(struct mq_policy *mq, struct entry *e,
static int pre_cache_entry_found(struct mq_policy *mq, struct entry *e,
bool can_migrate, bool discarded_oblock,
- int data_dir, struct policy_result *result)
+ int data_dir, struct policy_locker *locker,
+ struct policy_result *result)
{
int r = 0;
@@ -842,7 +853,7 @@ static int pre_cache_entry_found(struct mq_policy *mq, struct entry *e,
else {
requeue(mq, e);
- r = pre_cache_to_cache(mq, e, result);
+ r = pre_cache_to_cache(mq, e, locker, result);
}
return r;
@@ -872,6 +883,7 @@ static void insert_in_pre_cache(struct mq_policy *mq,
}
static void insert_in_cache(struct mq_policy *mq, dm_oblock_t oblock,
+ struct policy_locker *locker,
struct policy_result *result)
{
int r;
@@ -879,7 +891,7 @@ static void insert_in_cache(struct mq_policy *mq, dm_oblock_t oblock,
if (epool_empty(&mq->cache_pool)) {
result->op = POLICY_REPLACE;
- r = demote_cblock(mq, &result->old_oblock);
+ r = demote_cblock(mq, locker, &result->old_oblock);
if (unlikely(r)) {
result->op = POLICY_MISS;
insert_in_pre_cache(mq, oblock);
@@ -907,11 +919,12 @@ static void insert_in_cache(struct mq_policy *mq, dm_oblock_t oblock,
static int no_entry_found(struct mq_policy *mq, dm_oblock_t oblock,
bool can_migrate, bool discarded_oblock,
- int data_dir, struct policy_result *result)
+ int data_dir, struct policy_locker *locker,
+ struct policy_result *result)
{
if (adjusted_promote_threshold(mq, discarded_oblock, data_dir) <= 1) {
if (can_migrate)
- insert_in_cache(mq, oblock, result);
+ insert_in_cache(mq, oblock, locker, result);
else
return -EWOULDBLOCK;
} else {
@@ -928,7 +941,8 @@ static int no_entry_found(struct mq_policy *mq, dm_oblock_t oblock,
*/
static int map(struct mq_policy *mq, dm_oblock_t oblock,
bool can_migrate, bool discarded_oblock,
- int data_dir, struct policy_result *result)
+ int data_dir, struct policy_locker *locker,
+ struct policy_result *result)
{
int r = 0;
struct entry *e = hash_lookup(mq, oblock);
@@ -942,11 +956,11 @@ static int map(struct mq_policy *mq, dm_oblock_t oblock,
else if (e)
r = pre_cache_entry_found(mq, e, can_migrate, discarded_oblock,
- data_dir, result);
+ data_dir, locker, result);
else
r = no_entry_found(mq, oblock, can_migrate, discarded_oblock,
- data_dir, result);
+ data_dir, locker, result);
if (r == -EWOULDBLOCK)
result->op = POLICY_MISS;
@@ -1012,7 +1026,8 @@ static void copy_tick(struct mq_policy *mq)
static int mq_map(struct dm_cache_policy *p, dm_oblock_t oblock,
bool can_block, bool can_migrate, bool discarded_oblock,
- struct bio *bio, struct policy_result *result)
+ struct bio *bio, struct policy_locker *locker,
+ struct policy_result *result)
{
int r;
struct mq_policy *mq = to_mq_policy(p);
@@ -1028,7 +1043,7 @@ static int mq_map(struct dm_cache_policy *p, dm_oblock_t oblock,
iot_examine_bio(&mq->tracker, bio);
r = map(mq, oblock, can_migrate, discarded_oblock,
- bio_data_dir(bio), result);
+ bio_data_dir(bio), locker, result);
mutex_unlock(&mq->lock);
@@ -1221,7 +1236,7 @@ static int __mq_writeback_work(struct mq_policy *mq, dm_oblock_t *oblock,
}
static int mq_writeback_work(struct dm_cache_policy *p, dm_oblock_t *oblock,
- dm_cblock_t *cblock)
+ dm_cblock_t *cblock, bool critical_only)
{
int r;
struct mq_policy *mq = to_mq_policy(p);
@@ -1268,7 +1283,7 @@ static dm_cblock_t mq_residency(struct dm_cache_policy *p)
return r;
}
-static void mq_tick(struct dm_cache_policy *p)
+static void mq_tick(struct dm_cache_policy *p, bool can_block)
{
struct mq_policy *mq = to_mq_policy(p);
unsigned long flags;
@@ -1276,6 +1291,12 @@ static void mq_tick(struct dm_cache_policy *p)
spin_lock_irqsave(&mq->tick_lock, flags);
mq->tick_protected++;
spin_unlock_irqrestore(&mq->tick_lock, flags);
+
+ if (can_block) {
+ mutex_lock(&mq->lock);
+ copy_tick(mq);
+ mutex_unlock(&mq->lock);
+ }
}
static int mq_set_config_value(struct dm_cache_policy *p,
@@ -1308,22 +1329,24 @@ static int mq_set_config_value(struct dm_cache_policy *p,
return 0;
}
-static int mq_emit_config_values(struct dm_cache_policy *p, char *result, unsigned maxlen)
+static int mq_emit_config_values(struct dm_cache_policy *p, char *result,
+ unsigned maxlen, ssize_t *sz_ptr)
{
- ssize_t sz = 0;
+ ssize_t sz = *sz_ptr;
struct mq_policy *mq = to_mq_policy(p);
DMEMIT("10 random_threshold %u "
"sequential_threshold %u "
"discard_promote_adjustment %u "
"read_promote_adjustment %u "
- "write_promote_adjustment %u",
+ "write_promote_adjustment %u ",
mq->tracker.thresholds[PATTERN_RANDOM],
mq->tracker.thresholds[PATTERN_SEQUENTIAL],
mq->discard_promote_adjustment,
mq->read_promote_adjustment,
mq->write_promote_adjustment);
+ *sz_ptr = sz;
return 0;
}
@@ -1408,21 +1431,12 @@ bad_pre_cache_init:
static struct dm_cache_policy_type mq_policy_type = {
.name = "mq",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.hint_size = 4,
.owner = THIS_MODULE,
.create = mq_create
};
-static struct dm_cache_policy_type default_policy_type = {
- .name = "default",
- .version = {1, 3, 0},
- .hint_size = 4,
- .owner = THIS_MODULE,
- .create = mq_create,
- .real = &mq_policy_type
-};
-
static int __init mq_init(void)
{
int r;
@@ -1432,36 +1446,21 @@ static int __init mq_init(void)
__alignof__(struct entry),
0, NULL);
if (!mq_entry_cache)
- goto bad;
+ return -ENOMEM;
r = dm_cache_policy_register(&mq_policy_type);
if (r) {
DMERR("register failed %d", r);
- goto bad_register_mq;
- }
-
- r = dm_cache_policy_register(&default_policy_type);
- if (!r) {
- DMINFO("version %u.%u.%u loaded",
- mq_policy_type.version[0],
- mq_policy_type.version[1],
- mq_policy_type.version[2]);
- return 0;
+ kmem_cache_destroy(mq_entry_cache);
+ return -ENOMEM;
}
- DMERR("register failed (as default) %d", r);
-
- dm_cache_policy_unregister(&mq_policy_type);
-bad_register_mq:
- kmem_cache_destroy(mq_entry_cache);
-bad:
- return -ENOMEM;
+ return 0;
}
static void __exit mq_exit(void)
{
dm_cache_policy_unregister(&mq_policy_type);
- dm_cache_policy_unregister(&default_policy_type);
kmem_cache_destroy(mq_entry_cache);
}
diff --git a/drivers/md/dm-cache-policy-smq.c b/drivers/md/dm-cache-policy-smq.c
new file mode 100644
index 0000000..80f02d3
--- /dev/null
+++ b/drivers/md/dm-cache-policy-smq.c
@@ -0,0 +1,1791 @@
+/*
+ * Copyright (C) 2015 Red Hat. All rights reserved.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-cache-policy.h"
+#include "dm-cache-policy-internal.h"
+#include "dm.h"
+
+#include <linux/hash.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <linux/math64.h>
+
+#define DM_MSG_PREFIX "cache-policy-smq"
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Safe division functions that return zero on divide by zero.
+ */
+static unsigned safe_div(unsigned n, unsigned d)
+{
+ return d ? n / d : 0u;
+}
+
+static unsigned safe_mod(unsigned n, unsigned d)
+{
+ return d ? n % d : 0u;
+}
+
+/*----------------------------------------------------------------*/
+
+struct entry {
+ unsigned hash_next:28;
+ unsigned prev:28;
+ unsigned next:28;
+ unsigned level:7;
+ bool dirty:1;
+ bool allocated:1;
+ bool sentinel:1;
+
+ dm_oblock_t oblock;
+};
+
+/*----------------------------------------------------------------*/
+
+#define INDEXER_NULL ((1u << 28u) - 1u)
+
+/*
+ * An entry_space manages a set of entries that we use for the queues.
+ * The clean and dirty queues share entries, so this object is separate
+ * from the queue itself.
+ */
+struct entry_space {
+ struct entry *begin;
+ struct entry *end;
+};
+
+static int space_init(struct entry_space *es, unsigned nr_entries)
+{
+ if (!nr_entries) {
+ es->begin = es->end = NULL;
+ return 0;
+ }
+
+ es->begin = vzalloc(sizeof(struct entry) * nr_entries);
+ if (!es->begin)
+ return -ENOMEM;
+
+ es->end = es->begin + nr_entries;
+ return 0;
+}
+
+static void space_exit(struct entry_space *es)
+{
+ vfree(es->begin);
+}
+
+static struct entry *__get_entry(struct entry_space *es, unsigned block)
+{
+ struct entry *e;
+
+ e = es->begin + block;
+ BUG_ON(e >= es->end);
+
+ return e;
+}
+
+static unsigned to_index(struct entry_space *es, struct entry *e)
+{
+ BUG_ON(e < es->begin || e >= es->end);
+ return e - es->begin;
+}
+
+static struct entry *to_entry(struct entry_space *es, unsigned block)
+{
+ if (block == INDEXER_NULL)
+ return NULL;
+
+ return __get_entry(es, block);
+}
+
+/*----------------------------------------------------------------*/
+
+struct ilist {
+ unsigned nr_elts; /* excluding sentinel entries */
+ unsigned head, tail;
+};
+
+static void l_init(struct ilist *l)
+{
+ l->nr_elts = 0;
+ l->head = l->tail = INDEXER_NULL;
+}
+
+static struct entry *l_head(struct entry_space *es, struct ilist *l)
+{
+ return to_entry(es, l->head);
+}
+
+static struct entry *l_tail(struct entry_space *es, struct ilist *l)
+{
+ return to_entry(es, l->tail);
+}
+
+static struct entry *l_next(struct entry_space *es, struct entry *e)
+{
+ return to_entry(es, e->next);
+}
+
+static struct entry *l_prev(struct entry_space *es, struct entry *e)
+{
+ return to_entry(es, e->prev);
+}
+
+static bool l_empty(struct ilist *l)
+{
+ return l->head == INDEXER_NULL;
+}
+
+static void l_add_head(struct entry_space *es, struct ilist *l, struct entry *e)
+{
+ struct entry *head = l_head(es, l);
+
+ e->next = l->head;
+ e->prev = INDEXER_NULL;
+
+ if (head)
+ head->prev = l->head = to_index(es, e);
+ else
+ l->head = l->tail = to_index(es, e);
+
+ if (!e->sentinel)
+ l->nr_elts++;
+}
+
+static void l_add_tail(struct entry_space *es, struct ilist *l, struct entry *e)
+{
+ struct entry *tail = l_tail(es, l);
+
+ e->next = INDEXER_NULL;
+ e->prev = l->tail;
+
+ if (tail)
+ tail->next = l->tail = to_index(es, e);
+ else
+ l->head = l->tail = to_index(es, e);
+
+ if (!e->sentinel)
+ l->nr_elts++;
+}
+
+static void l_add_before(struct entry_space *es, struct ilist *l,
+ struct entry *old, struct entry *e)
+{
+ struct entry *prev = l_prev(es, old);
+
+ if (!prev)
+ l_add_head(es, l, e);
+
+ else {
+ e->prev = old->prev;
+ e->next = to_index(es, old);
+ prev->next = old->prev = to_index(es, e);
+
+ if (!e->sentinel)
+ l->nr_elts++;
+ }
+}
+
+static void l_del(struct entry_space *es, struct ilist *l, struct entry *e)
+{
+ struct entry *prev = l_prev(es, e);
+ struct entry *next = l_next(es, e);
+
+ if (prev)
+ prev->next = e->next;
+ else
+ l->head = e->next;
+
+ if (next)
+ next->prev = e->prev;
+ else
+ l->tail = e->prev;
+
+ if (!e->sentinel)
+ l->nr_elts--;
+}
+
+static struct entry *l_pop_tail(struct entry_space *es, struct ilist *l)
+{
+ struct entry *e;
+
+ for (e = l_tail(es, l); e; e = l_prev(es, e))
+ if (!e->sentinel) {
+ l_del(es, l, e);
+ return e;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * The stochastic-multi-queue is a set of lru lists stacked into levels.
+ * Entries are moved up levels when they are used, which loosely orders the
+ * most accessed entries in the top levels and least in the bottom. This
+ * structure is *much* better than a single lru list.
+ */
+#define MAX_LEVELS 64u
+
+struct queue {
+ struct entry_space *es;
+
+ unsigned nr_elts;
+ unsigned nr_levels;
+ struct ilist qs[MAX_LEVELS];
+
+ /*
+ * We maintain a count of the number of entries we would like in each
+ * level.
+ */
+ unsigned last_target_nr_elts;
+ unsigned nr_top_levels;
+ unsigned nr_in_top_levels;
+ unsigned target_count[MAX_LEVELS];
+};
+
+static void q_init(struct queue *q, struct entry_space *es, unsigned nr_levels)
+{
+ unsigned i;
+
+ q->es = es;
+ q->nr_elts = 0;
+ q->nr_levels = nr_levels;
+
+ for (i = 0; i < q->nr_levels; i++) {
+ l_init(q->qs + i);
+ q->target_count[i] = 0u;
+ }
+
+ q->last_target_nr_elts = 0u;
+ q->nr_top_levels = 0u;
+ q->nr_in_top_levels = 0u;
+}
+
+static unsigned q_size(struct queue *q)
+{
+ return q->nr_elts;
+}
+
+/*
+ * Insert an entry to the back of the given level.
+ */
+static void q_push(struct queue *q, struct entry *e)
+{
+ if (!e->sentinel)
+ q->nr_elts++;
+
+ l_add_tail(q->es, q->qs + e->level, e);
+}
+
+static void q_push_before(struct queue *q, struct entry *old, struct entry *e)
+{
+ if (!e->sentinel)
+ q->nr_elts++;
+
+ l_add_before(q->es, q->qs + e->level, old, e);
+}
+
+static void q_del(struct queue *q, struct entry *e)
+{
+ l_del(q->es, q->qs + e->level, e);
+ if (!e->sentinel)
+ q->nr_elts--;
+}
+
+/*
+ * Return the oldest entry of the lowest populated level.
+ */
+static struct entry *q_peek(struct queue *q, unsigned max_level, bool can_cross_sentinel)
+{
+ unsigned level;
+ struct entry *e;
+
+ max_level = min(max_level, q->nr_levels);
+
+ for (level = 0; level < max_level; level++)
+ for (e = l_head(q->es, q->qs + level); e; e = l_next(q->es, e)) {
+ if (e->sentinel) {
+ if (can_cross_sentinel)
+ continue;
+ else
+ break;
+ }
+
+ return e;
+ }
+
+ return NULL;
+}
+
+static struct entry *q_pop(struct queue *q)
+{
+ struct entry *e = q_peek(q, q->nr_levels, true);
+
+ if (e)
+ q_del(q, e);
+
+ return e;
+}
+
+/*
+ * Pops an entry from a level that is not past a sentinel.
+ */
+static struct entry *q_pop_old(struct queue *q, unsigned max_level)
+{
+ struct entry *e = q_peek(q, max_level, false);
+
+ if (e)
+ q_del(q, e);
+
+ return e;
+}
+
+/*
+ * This function assumes there is a non-sentinel entry to pop. It's only
+ * used by redistribute, so we know this is true. It also doesn't adjust
+ * the q->nr_elts count.
+ */
+static struct entry *__redist_pop_from(struct queue *q, unsigned level)
+{
+ struct entry *e;
+
+ for (; level < q->nr_levels; level++)
+ for (e = l_head(q->es, q->qs + level); e; e = l_next(q->es, e))
+ if (!e->sentinel) {
+ l_del(q->es, q->qs + e->level, e);
+ return e;
+ }
+
+ return NULL;
+}
+
+static void q_set_targets_subrange_(struct queue *q, unsigned nr_elts, unsigned lbegin, unsigned lend)
+{
+ unsigned level, nr_levels, entries_per_level, remainder;
+
+ BUG_ON(lbegin > lend);
+ BUG_ON(lend > q->nr_levels);
+ nr_levels = lend - lbegin;
+ entries_per_level = safe_div(nr_elts, nr_levels);
+ remainder = safe_mod(nr_elts, nr_levels);
+
+ for (level = lbegin; level < lend; level++)
+ q->target_count[level] =
+ (level < (lbegin + remainder)) ? entries_per_level + 1u : entries_per_level;
+}
+
+/*
+ * Typically we have fewer elements in the top few levels which allows us
+ * to adjust the promote threshold nicely.
+ */
+static void q_set_targets(struct queue *q)
+{
+ if (q->last_target_nr_elts == q->nr_elts)
+ return;
+
+ q->last_target_nr_elts = q->nr_elts;
+
+ if (q->nr_top_levels > q->nr_levels)
+ q_set_targets_subrange_(q, q->nr_elts, 0, q->nr_levels);
+
+ else {
+ q_set_targets_subrange_(q, q->nr_in_top_levels,
+ q->nr_levels - q->nr_top_levels, q->nr_levels);
+
+ if (q->nr_in_top_levels < q->nr_elts)
+ q_set_targets_subrange_(q, q->nr_elts - q->nr_in_top_levels,
+ 0, q->nr_levels - q->nr_top_levels);
+ else
+ q_set_targets_subrange_(q, 0, 0, q->nr_levels - q->nr_top_levels);
+ }
+}
+
+static void q_redistribute(struct queue *q)
+{
+ unsigned target, level;
+ struct ilist *l, *l_above;
+ struct entry *e;
+
+ q_set_targets(q);
+
+ for (level = 0u; level < q->nr_levels - 1u; level++) {
+ l = q->qs + level;
+ target = q->target_count[level];
+
+ /*
+ * Pull down some entries from the level above.
+ */
+ while (l->nr_elts < target) {
+ e = __redist_pop_from(q, level + 1u);
+ if (!e) {
+ /* bug in nr_elts */
+ break;
+ }
+
+ e->level = level;
+ l_add_tail(q->es, l, e);
+ }
+
+ /*
+ * Push some entries up.
+ */
+ l_above = q->qs + level + 1u;
+ while (l->nr_elts > target) {
+ e = l_pop_tail(q->es, l);
+
+ if (!e)
+ /* bug in nr_elts */
+ break;
+
+ e->level = level + 1u;
+ l_add_head(q->es, l_above, e);
+ }
+ }
+}
+
+static void q_requeue_before(struct queue *q, struct entry *dest, struct entry *e, unsigned extra_levels)
+{
+ struct entry *de;
+ unsigned new_level;
+
+ q_del(q, e);
+
+ if (extra_levels && (e->level < q->nr_levels - 1u)) {
+ new_level = min(q->nr_levels - 1u, e->level + extra_levels);
+ for (de = l_head(q->es, q->qs + new_level); de; de = l_next(q->es, de)) {
+ if (de->sentinel)
+ continue;
+
+ q_del(q, de);
+ de->level = e->level;
+
+ if (dest)
+ q_push_before(q, dest, de);
+ else
+ q_push(q, de);
+ break;
+ }
+
+ e->level = new_level;
+ }
+
+ q_push(q, e);
+}
+
+static void q_requeue(struct queue *q, struct entry *e, unsigned extra_levels)
+{
+ q_requeue_before(q, NULL, e, extra_levels);
+}
+
+/*----------------------------------------------------------------*/
+
+#define FP_SHIFT 8
+#define SIXTEENTH (1u << (FP_SHIFT - 4u))
+#define EIGHTH (1u << (FP_SHIFT - 3u))
+
+struct stats {
+ unsigned hit_threshold;
+ unsigned hits;
+ unsigned misses;
+};
+
+enum performance {
+ Q_POOR,
+ Q_FAIR,
+ Q_WELL
+};
+
+static void stats_init(struct stats *s, unsigned nr_levels)
+{
+ s->hit_threshold = (nr_levels * 3u) / 4u;
+ s->hits = 0u;
+ s->misses = 0u;
+}
+
+static void stats_reset(struct stats *s)
+{
+ s->hits = s->misses = 0u;
+}
+
+static void stats_level_accessed(struct stats *s, unsigned level)
+{
+ if (level >= s->hit_threshold)
+ s->hits++;
+ else
+ s->misses++;
+}
+
+static void stats_miss(struct stats *s)
+{
+ s->misses++;
+}
+
+/*
+ * There are times when we don't have any confidence in the hotspot queue.
+ * Such as when a fresh cache is created and the blocks have been spread
+ * out across the levels, or if an io load changes. We detect this by
+ * seeing how often a lookup is in the top levels of the hotspot queue.
+ */
+static enum performance stats_assess(struct stats *s)
+{
+ unsigned confidence = safe_div(s->hits << FP_SHIFT, s->hits + s->misses);
+
+ if (confidence < SIXTEENTH)
+ return Q_POOR;
+
+ else if (confidence < EIGHTH)
+ return Q_FAIR;
+
+ else
+ return Q_WELL;
+}
+
+/*----------------------------------------------------------------*/
+
+struct hash_table {
+ struct entry_space *es;
+ unsigned long long hash_bits;
+ unsigned *buckets;
+};
+
+/*
+ * All cache entries are stored in a chained hash table. To save space we
+ * use indexing again, and only store indexes to the next entry.
+ */
+static int h_init(struct hash_table *ht, struct entry_space *es, unsigned nr_entries)
+{
+ unsigned i, nr_buckets;
+
+ ht->es = es;
+ nr_buckets = roundup_pow_of_two(max(nr_entries / 4u, 16u));
+ ht->hash_bits = ffs(nr_buckets) - 1;
+
+ ht->buckets = vmalloc(sizeof(*ht->buckets) * nr_buckets);
+ if (!ht->buckets)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_buckets; i++)
+ ht->buckets[i] = INDEXER_NULL;
+
+ return 0;
+}
+
+static void h_exit(struct hash_table *ht)
+{
+ vfree(ht->buckets);
+}
+
+static struct entry *h_head(struct hash_table *ht, unsigned bucket)
+{
+ return to_entry(ht->es, ht->buckets[bucket]);
+}
+
+static struct entry *h_next(struct hash_table *ht, struct entry *e)
+{
+ return to_entry(ht->es, e->hash_next);
+}
+
+static void __h_insert(struct hash_table *ht, unsigned bucket, struct entry *e)
+{
+ e->hash_next = ht->buckets[bucket];
+ ht->buckets[bucket] = to_index(ht->es, e);
+}
+
+static void h_insert(struct hash_table *ht, struct entry *e)
+{
+ unsigned h = hash_64(from_oblock(e->oblock), ht->hash_bits);
+ __h_insert(ht, h, e);
+}
+
+static struct entry *__h_lookup(struct hash_table *ht, unsigned h, dm_oblock_t oblock,
+ struct entry **prev)
+{
+ struct entry *e;
+
+ *prev = NULL;
+ for (e = h_head(ht, h); e; e = h_next(ht, e)) {
+ if (e->oblock == oblock)
+ return e;
+
+ *prev = e;
+ }
+
+ return NULL;
+}
+
+static void __h_unlink(struct hash_table *ht, unsigned h,
+ struct entry *e, struct entry *prev)
+{
+ if (prev)
+ prev->hash_next = e->hash_next;
+ else
+ ht->buckets[h] = e->hash_next;
+}
+
+/*
+ * Also moves each entry to the front of the bucket.
+ */
+static struct entry *h_lookup(struct hash_table *ht, dm_oblock_t oblock)
+{
+ struct entry *e, *prev;
+ unsigned h = hash_64(from_oblock(oblock), ht->hash_bits);
+
+ e = __h_lookup(ht, h, oblock, &prev);
+ if (e && prev) {
+ /*
+ * Move to the front because this entry is likely
+ * to be hit again.
+ */
+ __h_unlink(ht, h, e, prev);
+ __h_insert(ht, h, e);
+ }
+
+ return e;
+}
+
+static void h_remove(struct hash_table *ht, struct entry *e)
+{
+ unsigned h = hash_64(from_oblock(e->oblock), ht->hash_bits);
+ struct entry *prev;
+
+ /*
+ * The down side of using a singly linked list is we have to
+ * iterate the bucket to remove an item.
+ */
+ e = __h_lookup(ht, h, e->oblock, &prev);
+ if (e)
+ __h_unlink(ht, h, e, prev);
+}
+
+/*----------------------------------------------------------------*/
+
+struct entry_alloc {
+ struct entry_space *es;
+ unsigned begin;
+
+ unsigned nr_allocated;
+ struct ilist free;
+};
+
+static void init_allocator(struct entry_alloc *ea, struct entry_space *es,
+ unsigned begin, unsigned end)
+{
+ unsigned i;
+
+ ea->es = es;
+ ea->nr_allocated = 0u;
+ ea->begin = begin;
+
+ l_init(&ea->free);
+ for (i = begin; i != end; i++)
+ l_add_tail(ea->es, &ea->free, __get_entry(ea->es, i));
+}
+
+static void init_entry(struct entry *e)
+{
+ /*
+ * We can't memset because that would clear the hotspot and
+ * sentinel bits which remain constant.
+ */
+ e->hash_next = INDEXER_NULL;
+ e->next = INDEXER_NULL;
+ e->prev = INDEXER_NULL;
+ e->level = 0u;
+ e->allocated = true;
+}
+
+static struct entry *alloc_entry(struct entry_alloc *ea)
+{
+ struct entry *e;
+
+ if (l_empty(&ea->free))
+ return NULL;
+
+ e = l_pop_tail(ea->es, &ea->free);
+ init_entry(e);
+ ea->nr_allocated++;
+
+ return e;
+}
+
+/*
+ * This assumes the cblock hasn't already been allocated.
+ */
+static struct entry *alloc_particular_entry(struct entry_alloc *ea, unsigned i)
+{
+ struct entry *e = __get_entry(ea->es, ea->begin + i);
+
+ BUG_ON(e->allocated);
+
+ l_del(ea->es, &ea->free, e);
+ init_entry(e);
+ ea->nr_allocated++;
+
+ return e;
+}
+
+static void free_entry(struct entry_alloc *ea, struct entry *e)
+{
+ BUG_ON(!ea->nr_allocated);
+ BUG_ON(!e->allocated);
+
+ ea->nr_allocated--;
+ e->allocated = false;
+ l_add_tail(ea->es, &ea->free, e);
+}
+
+static bool allocator_empty(struct entry_alloc *ea)
+{
+ return l_empty(&ea->free);
+}
+
+static unsigned get_index(struct entry_alloc *ea, struct entry *e)
+{
+ return to_index(ea->es, e) - ea->begin;
+}
+
+static struct entry *get_entry(struct entry_alloc *ea, unsigned index)
+{
+ return __get_entry(ea->es, ea->begin + index);
+}
+
+/*----------------------------------------------------------------*/
+
+#define NR_HOTSPOT_LEVELS 64u
+#define NR_CACHE_LEVELS 64u
+
+#define WRITEBACK_PERIOD (10 * HZ)
+#define DEMOTE_PERIOD (60 * HZ)
+
+#define HOTSPOT_UPDATE_PERIOD (HZ)
+#define CACHE_UPDATE_PERIOD (10u * HZ)
+
+struct smq_policy {
+ struct dm_cache_policy policy;
+
+ /* protects everything */
+ struct mutex lock;
+ dm_cblock_t cache_size;
+ sector_t cache_block_size;
+
+ sector_t hotspot_block_size;
+ unsigned nr_hotspot_blocks;
+ unsigned cache_blocks_per_hotspot_block;
+ unsigned hotspot_level_jump;
+
+ struct entry_space es;
+ struct entry_alloc writeback_sentinel_alloc;
+ struct entry_alloc demote_sentinel_alloc;
+ struct entry_alloc hotspot_alloc;
+ struct entry_alloc cache_alloc;
+
+ unsigned long *hotspot_hit_bits;
+ unsigned long *cache_hit_bits;
+
+ /*
+ * We maintain three queues of entries. The cache proper,
+ * consisting of a clean and dirty queue, containing the currently
+ * active mappings. The hotspot queue uses a larger block size to
+ * track blocks that are being hit frequently and potential
+ * candidates for promotion to the cache.
+ */
+ struct queue hotspot;
+ struct queue clean;
+ struct queue dirty;
+
+ struct stats hotspot_stats;
+ struct stats cache_stats;
+
+ /*
+ * Keeps track of time, incremented by the core. We use this to
+ * avoid attributing multiple hits within the same tick.
+ *
+ * Access to tick_protected should be done with the spin lock held.
+ * It's copied to tick at the start of the map function (within the
+ * mutex).
+ */
+ spinlock_t tick_lock;
+ unsigned tick_protected;
+ unsigned tick;
+
+ /*
+ * The hash tables allows us to quickly find an entry by origin
+ * block.
+ */
+ struct hash_table table;
+ struct hash_table hotspot_table;
+
+ bool current_writeback_sentinels;
+ unsigned long next_writeback_period;
+
+ bool current_demote_sentinels;
+ unsigned long next_demote_period;
+
+ unsigned write_promote_level;
+ unsigned read_promote_level;
+
+ unsigned long next_hotspot_period;
+ unsigned long next_cache_period;
+};
+
+/*----------------------------------------------------------------*/
+
+static struct entry *get_sentinel(struct entry_alloc *ea, unsigned level, bool which)
+{
+ return get_entry(ea, which ? level : NR_CACHE_LEVELS + level);
+}
+
+static struct entry *writeback_sentinel(struct smq_policy *mq, unsigned level)
+{
+ return get_sentinel(&mq->writeback_sentinel_alloc, level, mq->current_writeback_sentinels);
+}
+
+static struct entry *demote_sentinel(struct smq_policy *mq, unsigned level)
+{
+ return get_sentinel(&mq->demote_sentinel_alloc, level, mq->current_demote_sentinels);
+}
+
+static void __update_writeback_sentinels(struct smq_policy *mq)
+{
+ unsigned level;
+ struct queue *q = &mq->dirty;
+ struct entry *sentinel;
+
+ for (level = 0; level < q->nr_levels; level++) {
+ sentinel = writeback_sentinel(mq, level);
+ q_del(q, sentinel);
+ q_push(q, sentinel);
+ }
+}
+
+static void __update_demote_sentinels(struct smq_policy *mq)
+{
+ unsigned level;
+ struct queue *q = &mq->clean;
+ struct entry *sentinel;
+
+ for (level = 0; level < q->nr_levels; level++) {
+ sentinel = demote_sentinel(mq, level);
+ q_del(q, sentinel);
+ q_push(q, sentinel);
+ }
+}
+
+static void update_sentinels(struct smq_policy *mq)
+{
+ if (time_after(jiffies, mq->next_writeback_period)) {
+ __update_writeback_sentinels(mq);
+ mq->next_writeback_period = jiffies + WRITEBACK_PERIOD;
+ mq->current_writeback_sentinels = !mq->current_writeback_sentinels;
+ }
+
+ if (time_after(jiffies, mq->next_demote_period)) {
+ __update_demote_sentinels(mq);
+ mq->next_demote_period = jiffies + DEMOTE_PERIOD;
+ mq->current_demote_sentinels = !mq->current_demote_sentinels;
+ }
+}
+
+static void __sentinels_init(struct smq_policy *mq)
+{
+ unsigned level;
+ struct entry *sentinel;
+
+ for (level = 0; level < NR_CACHE_LEVELS; level++) {
+ sentinel = writeback_sentinel(mq, level);
+ sentinel->level = level;
+ q_push(&mq->dirty, sentinel);
+
+ sentinel = demote_sentinel(mq, level);
+ sentinel->level = level;
+ q_push(&mq->clean, sentinel);
+ }
+}
+
+static void sentinels_init(struct smq_policy *mq)
+{
+ mq->next_writeback_period = jiffies + WRITEBACK_PERIOD;
+ mq->next_demote_period = jiffies + DEMOTE_PERIOD;
+
+ mq->current_writeback_sentinels = false;
+ mq->current_demote_sentinels = false;
+ __sentinels_init(mq);
+
+ mq->current_writeback_sentinels = !mq->current_writeback_sentinels;
+ mq->current_demote_sentinels = !mq->current_demote_sentinels;
+ __sentinels_init(mq);
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * These methods tie together the dirty queue, clean queue and hash table.
+ */
+static void push_new(struct smq_policy *mq, struct entry *e)
+{
+ struct queue *q = e->dirty ? &mq->dirty : &mq->clean;
+ h_insert(&mq->table, e);
+ q_push(q, e);
+}
+
+static void push(struct smq_policy *mq, struct entry *e)
+{
+ struct entry *sentinel;
+
+ h_insert(&mq->table, e);
+
+ /*
+ * Punch this into the queue just in front of the sentinel, to
+ * ensure it's cleaned straight away.
+ */
+ if (e->dirty) {
+ sentinel = writeback_sentinel(mq, e->level);
+ q_push_before(&mq->dirty, sentinel, e);
+ } else {
+ sentinel = demote_sentinel(mq, e->level);
+ q_push_before(&mq->clean, sentinel, e);
+ }
+}
+
+/*
+ * Removes an entry from cache. Removes from the hash table.
+ */
+static void __del(struct smq_policy *mq, struct queue *q, struct entry *e)
+{
+ q_del(q, e);
+ h_remove(&mq->table, e);
+}
+
+static void del(struct smq_policy *mq, struct entry *e)
+{
+ __del(mq, e->dirty ? &mq->dirty : &mq->clean, e);
+}
+
+static struct entry *pop_old(struct smq_policy *mq, struct queue *q, unsigned max_level)
+{
+ struct entry *e = q_pop_old(q, max_level);
+ if (e)
+ h_remove(&mq->table, e);
+ return e;
+}
+
+static dm_cblock_t infer_cblock(struct smq_policy *mq, struct entry *e)
+{
+ return to_cblock(get_index(&mq->cache_alloc, e));
+}
+
+static void requeue(struct smq_policy *mq, struct entry *e)
+{
+ struct entry *sentinel;
+
+ if (!test_and_set_bit(from_cblock(infer_cblock(mq, e)), mq->cache_hit_bits)) {
+ if (e->dirty) {
+ sentinel = writeback_sentinel(mq, e->level);
+ q_requeue_before(&mq->dirty, sentinel, e, 1u);
+ } else {
+ sentinel = demote_sentinel(mq, e->level);
+ q_requeue_before(&mq->clean, sentinel, e, 1u);
+ }
+ }
+}
+
+static unsigned default_promote_level(struct smq_policy *mq)
+{
+ /*
+ * The promote level depends on the current performance of the
+ * cache.
+ *
+ * If the cache is performing badly, then we can't afford
+ * to promote much without causing performance to drop below that
+ * of the origin device.
+ *
+ * If the cache is performing well, then we don't need to promote
+ * much. If it isn't broken, don't fix it.
+ *
+ * If the cache is middling then we promote more.
+ *
+ * This scheme reminds me of a graph of entropy vs probability of a
+ * binary variable.
+ */
+ static unsigned table[] = {1, 1, 1, 2, 4, 6, 7, 8, 7, 6, 4, 4, 3, 3, 2, 2, 1};
+
+ unsigned hits = mq->cache_stats.hits;
+ unsigned misses = mq->cache_stats.misses;
+ unsigned index = safe_div(hits << 4u, hits + misses);
+ return table[index];
+}
+
+static void update_promote_levels(struct smq_policy *mq)
+{
+ /*
+ * If there are unused cache entries then we want to be really
+ * eager to promote.
+ */
+ unsigned threshold_level = allocator_empty(&mq->cache_alloc) ?
+ default_promote_level(mq) : (NR_HOTSPOT_LEVELS / 2u);
+
+ /*
+ * If the hotspot queue is performing badly then we have little
+ * confidence that we know which blocks to promote. So we cut down
+ * the amount of promotions.
+ */
+ switch (stats_assess(&mq->hotspot_stats)) {
+ case Q_POOR:
+ threshold_level /= 4u;
+ break;
+
+ case Q_FAIR:
+ threshold_level /= 2u;
+ break;
+
+ case Q_WELL:
+ break;
+ }
+
+ mq->read_promote_level = NR_HOTSPOT_LEVELS - threshold_level;
+ mq->write_promote_level = (NR_HOTSPOT_LEVELS - threshold_level) + 2u;
+}
+
+/*
+ * If the hotspot queue is performing badly, then we try and move entries
+ * around more quickly.
+ */
+static void update_level_jump(struct smq_policy *mq)
+{
+ switch (stats_assess(&mq->hotspot_stats)) {
+ case Q_POOR:
+ mq->hotspot_level_jump = 4u;
+ break;
+
+ case Q_FAIR:
+ mq->hotspot_level_jump = 2u;
+ break;
+
+ case Q_WELL:
+ mq->hotspot_level_jump = 1u;
+ break;
+ }
+}
+
+static void end_hotspot_period(struct smq_policy *mq)
+{
+ clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks);
+ update_promote_levels(mq);
+
+ if (time_after(jiffies, mq->next_hotspot_period)) {
+ update_level_jump(mq);
+ q_redistribute(&mq->hotspot);
+ stats_reset(&mq->hotspot_stats);
+ mq->next_hotspot_period = jiffies + HOTSPOT_UPDATE_PERIOD;
+ }
+}
+
+static void end_cache_period(struct smq_policy *mq)
+{
+ if (time_after(jiffies, mq->next_cache_period)) {
+ clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size));
+
+ q_redistribute(&mq->dirty);
+ q_redistribute(&mq->clean);
+ stats_reset(&mq->cache_stats);
+
+ mq->next_cache_period = jiffies + CACHE_UPDATE_PERIOD;
+ }
+}
+
+static int demote_cblock(struct smq_policy *mq,
+ struct policy_locker *locker,
+ dm_oblock_t *oblock)
+{
+ struct entry *demoted = q_peek(&mq->clean, mq->clean.nr_levels, false);
+ if (!demoted)
+ /*
+ * We could get a block from mq->dirty, but that
+ * would add extra latency to the triggering bio as it
+ * waits for the writeback. Better to not promote this
+ * time and hope there's a clean block next time this block
+ * is hit.
+ */
+ return -ENOSPC;
+
+ if (locker->fn(locker, demoted->oblock))
+ /*
+ * We couldn't lock this block.
+ */
+ return -EBUSY;
+
+ del(mq, demoted);
+ *oblock = demoted->oblock;
+ free_entry(&mq->cache_alloc, demoted);
+
+ return 0;
+}
+
+enum promote_result {
+ PROMOTE_NOT,
+ PROMOTE_TEMPORARY,
+ PROMOTE_PERMANENT
+};
+
+/*
+ * Converts a boolean into a promote result.
+ */
+static enum promote_result maybe_promote(bool promote)
+{
+ return promote ? PROMOTE_PERMANENT : PROMOTE_NOT;
+}
+
+static enum promote_result should_promote(struct smq_policy *mq, struct entry *hs_e, struct bio *bio,
+ bool fast_promote)
+{
+ if (bio_data_dir(bio) == WRITE) {
+ if (!allocator_empty(&mq->cache_alloc) && fast_promote)
+ return PROMOTE_TEMPORARY;
+
+ else
+ return maybe_promote(hs_e->level >= mq->write_promote_level);
+ } else
+ return maybe_promote(hs_e->level >= mq->read_promote_level);
+}
+
+static void insert_in_cache(struct smq_policy *mq, dm_oblock_t oblock,
+ struct policy_locker *locker,
+ struct policy_result *result, enum promote_result pr)
+{
+ int r;
+ struct entry *e;
+
+ if (allocator_empty(&mq->cache_alloc)) {
+ result->op = POLICY_REPLACE;
+ r = demote_cblock(mq, locker, &result->old_oblock);
+ if (r) {
+ result->op = POLICY_MISS;
+ return;
+ }
+
+ } else
+ result->op = POLICY_NEW;
+
+ e = alloc_entry(&mq->cache_alloc);
+ BUG_ON(!e);
+ e->oblock = oblock;
+
+ if (pr == PROMOTE_TEMPORARY)
+ push(mq, e);
+ else
+ push_new(mq, e);
+
+ result->cblock = infer_cblock(mq, e);
+}
+
+static dm_oblock_t to_hblock(struct smq_policy *mq, dm_oblock_t b)
+{
+ sector_t r = from_oblock(b);
+ (void) sector_div(r, mq->cache_blocks_per_hotspot_block);
+ return to_oblock(r);
+}
+
+static struct entry *update_hotspot_queue(struct smq_policy *mq, dm_oblock_t b, struct bio *bio)
+{
+ unsigned hi;
+ dm_oblock_t hb = to_hblock(mq, b);
+ struct entry *e = h_lookup(&mq->hotspot_table, hb);
+
+ if (e) {
+ stats_level_accessed(&mq->hotspot_stats, e->level);
+
+ hi = get_index(&mq->hotspot_alloc, e);
+ q_requeue(&mq->hotspot, e,
+ test_and_set_bit(hi, mq->hotspot_hit_bits) ?
+ 0u : mq->hotspot_level_jump);
+
+ } else {
+ stats_miss(&mq->hotspot_stats);
+
+ e = alloc_entry(&mq->hotspot_alloc);
+ if (!e) {
+ e = q_pop(&mq->hotspot);
+ if (e) {
+ h_remove(&mq->hotspot_table, e);
+ hi = get_index(&mq->hotspot_alloc, e);
+ clear_bit(hi, mq->hotspot_hit_bits);
+ }
+
+ }
+
+ if (e) {
+ e->oblock = hb;
+ q_push(&mq->hotspot, e);
+ h_insert(&mq->hotspot_table, e);
+ }
+ }
+
+ return e;
+}
+
+/*
+ * Looks the oblock up in the hash table, then decides whether to put in
+ * pre_cache, or cache etc.
+ */
+static int map(struct smq_policy *mq, struct bio *bio, dm_oblock_t oblock,
+ bool can_migrate, bool fast_promote,
+ struct policy_locker *locker, struct policy_result *result)
+{
+ struct entry *e, *hs_e;
+ enum promote_result pr;
+
+ hs_e = update_hotspot_queue(mq, oblock, bio);
+
+ e = h_lookup(&mq->table, oblock);
+ if (e) {
+ stats_level_accessed(&mq->cache_stats, e->level);
+
+ requeue(mq, e);
+ result->op = POLICY_HIT;
+ result->cblock = infer_cblock(mq, e);
+
+ } else {
+ stats_miss(&mq->cache_stats);
+
+ pr = should_promote(mq, hs_e, bio, fast_promote);
+ if (pr == PROMOTE_NOT)
+ result->op = POLICY_MISS;
+
+ else {
+ if (!can_migrate) {
+ result->op = POLICY_MISS;
+ return -EWOULDBLOCK;
+ }
+
+ insert_in_cache(mq, oblock, locker, result, pr);
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Public interface, via the policy struct. See dm-cache-policy.h for a
+ * description of these.
+ */
+
+static struct smq_policy *to_smq_policy(struct dm_cache_policy *p)
+{
+ return container_of(p, struct smq_policy, policy);
+}
+
+static void smq_destroy(struct dm_cache_policy *p)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+
+ h_exit(&mq->hotspot_table);
+ h_exit(&mq->table);
+ free_bitset(mq->hotspot_hit_bits);
+ free_bitset(mq->cache_hit_bits);
+ space_exit(&mq->es);
+ kfree(mq);
+}
+
+static void copy_tick(struct smq_policy *mq)
+{
+ unsigned long flags, tick;
+
+ spin_lock_irqsave(&mq->tick_lock, flags);
+ tick = mq->tick_protected;
+ if (tick != mq->tick) {
+ update_sentinels(mq);
+ end_hotspot_period(mq);
+ end_cache_period(mq);
+ mq->tick = tick;
+ }
+ spin_unlock_irqrestore(&mq->tick_lock, flags);
+}
+
+static bool maybe_lock(struct smq_policy *mq, bool can_block)
+{
+ if (can_block) {
+ mutex_lock(&mq->lock);
+ return true;
+ } else
+ return mutex_trylock(&mq->lock);
+}
+
+static int smq_map(struct dm_cache_policy *p, dm_oblock_t oblock,
+ bool can_block, bool can_migrate, bool fast_promote,
+ struct bio *bio, struct policy_locker *locker,
+ struct policy_result *result)
+{
+ int r;
+ struct smq_policy *mq = to_smq_policy(p);
+
+ result->op = POLICY_MISS;
+
+ if (!maybe_lock(mq, can_block))
+ return -EWOULDBLOCK;
+
+ copy_tick(mq);
+ r = map(mq, bio, oblock, can_migrate, fast_promote, locker, result);
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+static int smq_lookup(struct dm_cache_policy *p, dm_oblock_t oblock, dm_cblock_t *cblock)
+{
+ int r;
+ struct smq_policy *mq = to_smq_policy(p);
+ struct entry *e;
+
+ if (!mutex_trylock(&mq->lock))
+ return -EWOULDBLOCK;
+
+ e = h_lookup(&mq->table, oblock);
+ if (e) {
+ *cblock = infer_cblock(mq, e);
+ r = 0;
+ } else
+ r = -ENOENT;
+
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+static void __smq_set_clear_dirty(struct smq_policy *mq, dm_oblock_t oblock, bool set)
+{
+ struct entry *e;
+
+ e = h_lookup(&mq->table, oblock);
+ BUG_ON(!e);
+
+ del(mq, e);
+ e->dirty = set;
+ push(mq, e);
+}
+
+static void smq_set_dirty(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ __smq_set_clear_dirty(mq, oblock, true);
+ mutex_unlock(&mq->lock);
+}
+
+static void smq_clear_dirty(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ __smq_set_clear_dirty(mq, oblock, false);
+ mutex_unlock(&mq->lock);
+}
+
+static int smq_load_mapping(struct dm_cache_policy *p,
+ dm_oblock_t oblock, dm_cblock_t cblock,
+ uint32_t hint, bool hint_valid)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+ struct entry *e;
+
+ e = alloc_particular_entry(&mq->cache_alloc, from_cblock(cblock));
+ e->oblock = oblock;
+ e->dirty = false; /* this gets corrected in a minute */
+ e->level = hint_valid ? min(hint, NR_CACHE_LEVELS - 1) : 1;
+ push(mq, e);
+
+ return 0;
+}
+
+static int smq_save_hints(struct smq_policy *mq, struct queue *q,
+ policy_walk_fn fn, void *context)
+{
+ int r;
+ unsigned level;
+ struct entry *e;
+
+ for (level = 0; level < q->nr_levels; level++)
+ for (e = l_head(q->es, q->qs + level); e; e = l_next(q->es, e)) {
+ if (!e->sentinel) {
+ r = fn(context, infer_cblock(mq, e),
+ e->oblock, e->level);
+ if (r)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int smq_walk_mappings(struct dm_cache_policy *p, policy_walk_fn fn,
+ void *context)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+ int r = 0;
+
+ mutex_lock(&mq->lock);
+
+ r = smq_save_hints(mq, &mq->clean, fn, context);
+ if (!r)
+ r = smq_save_hints(mq, &mq->dirty, fn, context);
+
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+static void __remove_mapping(struct smq_policy *mq, dm_oblock_t oblock)
+{
+ struct entry *e;
+
+ e = h_lookup(&mq->table, oblock);
+ BUG_ON(!e);
+
+ del(mq, e);
+ free_entry(&mq->cache_alloc, e);
+}
+
+static void smq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ __remove_mapping(mq, oblock);
+ mutex_unlock(&mq->lock);
+}
+
+static int __remove_cblock(struct smq_policy *mq, dm_cblock_t cblock)
+{
+ struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock));
+
+ if (!e || !e->allocated)
+ return -ENODATA;
+
+ del(mq, e);
+ free_entry(&mq->cache_alloc, e);
+
+ return 0;
+}
+
+static int smq_remove_cblock(struct dm_cache_policy *p, dm_cblock_t cblock)
+{
+ int r;
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ r = __remove_cblock(mq, cblock);
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+
+#define CLEAN_TARGET_CRITICAL 5u /* percent */
+
+static bool clean_target_met(struct smq_policy *mq, bool critical)
+{
+ if (critical) {
+ /*
+ * Cache entries may not be populated. So we're cannot rely on the
+ * size of the clean queue.
+ */
+ unsigned nr_clean = from_cblock(mq->cache_size) - q_size(&mq->dirty);
+ unsigned target = from_cblock(mq->cache_size) * CLEAN_TARGET_CRITICAL / 100u;
+
+ return nr_clean >= target;
+ } else
+ return !q_size(&mq->dirty);
+}
+
+static int __smq_writeback_work(struct smq_policy *mq, dm_oblock_t *oblock,
+ dm_cblock_t *cblock, bool critical_only)
+{
+ struct entry *e = NULL;
+ bool target_met = clean_target_met(mq, critical_only);
+
+ if (critical_only)
+ /*
+ * Always try and keep the bottom level clean.
+ */
+ e = pop_old(mq, &mq->dirty, target_met ? 1u : mq->dirty.nr_levels);
+
+ else
+ e = pop_old(mq, &mq->dirty, mq->dirty.nr_levels);
+
+ if (!e)
+ return -ENODATA;
+
+ *oblock = e->oblock;
+ *cblock = infer_cblock(mq, e);
+ e->dirty = false;
+ push_new(mq, e);
+
+ return 0;
+}
+
+static int smq_writeback_work(struct dm_cache_policy *p, dm_oblock_t *oblock,
+ dm_cblock_t *cblock, bool critical_only)
+{
+ int r;
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ r = __smq_writeback_work(mq, oblock, cblock, critical_only);
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+static void __force_mapping(struct smq_policy *mq,
+ dm_oblock_t current_oblock, dm_oblock_t new_oblock)
+{
+ struct entry *e = h_lookup(&mq->table, current_oblock);
+
+ if (e) {
+ del(mq, e);
+ e->oblock = new_oblock;
+ e->dirty = true;
+ push(mq, e);
+ }
+}
+
+static void smq_force_mapping(struct dm_cache_policy *p,
+ dm_oblock_t current_oblock, dm_oblock_t new_oblock)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ __force_mapping(mq, current_oblock, new_oblock);
+ mutex_unlock(&mq->lock);
+}
+
+static dm_cblock_t smq_residency(struct dm_cache_policy *p)
+{
+ dm_cblock_t r;
+ struct smq_policy *mq = to_smq_policy(p);
+
+ mutex_lock(&mq->lock);
+ r = to_cblock(mq->cache_alloc.nr_allocated);
+ mutex_unlock(&mq->lock);
+
+ return r;
+}
+
+static void smq_tick(struct dm_cache_policy *p, bool can_block)
+{
+ struct smq_policy *mq = to_smq_policy(p);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mq->tick_lock, flags);
+ mq->tick_protected++;
+ spin_unlock_irqrestore(&mq->tick_lock, flags);
+
+ if (can_block) {
+ mutex_lock(&mq->lock);
+ copy_tick(mq);
+ mutex_unlock(&mq->lock);
+ }
+}
+
+/* Init the policy plugin interface function pointers. */
+static void init_policy_functions(struct smq_policy *mq)
+{
+ mq->policy.destroy = smq_destroy;
+ mq->policy.map = smq_map;
+ mq->policy.lookup = smq_lookup;
+ mq->policy.set_dirty = smq_set_dirty;
+ mq->policy.clear_dirty = smq_clear_dirty;
+ mq->policy.load_mapping = smq_load_mapping;
+ mq->policy.walk_mappings = smq_walk_mappings;
+ mq->policy.remove_mapping = smq_remove_mapping;
+ mq->policy.remove_cblock = smq_remove_cblock;
+ mq->policy.writeback_work = smq_writeback_work;
+ mq->policy.force_mapping = smq_force_mapping;
+ mq->policy.residency = smq_residency;
+ mq->policy.tick = smq_tick;
+}
+
+static bool too_many_hotspot_blocks(sector_t origin_size,
+ sector_t hotspot_block_size,
+ unsigned nr_hotspot_blocks)
+{
+ return (hotspot_block_size * nr_hotspot_blocks) > origin_size;
+}
+
+static void calc_hotspot_params(sector_t origin_size,
+ sector_t cache_block_size,
+ unsigned nr_cache_blocks,
+ sector_t *hotspot_block_size,
+ unsigned *nr_hotspot_blocks)
+{
+ *hotspot_block_size = cache_block_size * 16u;
+ *nr_hotspot_blocks = max(nr_cache_blocks / 4u, 1024u);
+
+ while ((*hotspot_block_size > cache_block_size) &&
+ too_many_hotspot_blocks(origin_size, *hotspot_block_size, *nr_hotspot_blocks))
+ *hotspot_block_size /= 2u;
+}
+
+static struct dm_cache_policy *smq_create(dm_cblock_t cache_size,
+ sector_t origin_size,
+ sector_t cache_block_size)
+{
+ unsigned i;
+ unsigned nr_sentinels_per_queue = 2u * NR_CACHE_LEVELS;
+ unsigned total_sentinels = 2u * nr_sentinels_per_queue;
+ struct smq_policy *mq = kzalloc(sizeof(*mq), GFP_KERNEL);
+
+ if (!mq)
+ return NULL;
+
+ init_policy_functions(mq);
+ mq->cache_size = cache_size;
+ mq->cache_block_size = cache_block_size;
+
+ calc_hotspot_params(origin_size, cache_block_size, from_cblock(cache_size),
+ &mq->hotspot_block_size, &mq->nr_hotspot_blocks);
+
+ mq->cache_blocks_per_hotspot_block = div64_u64(mq->hotspot_block_size, mq->cache_block_size);
+ mq->hotspot_level_jump = 1u;
+ if (space_init(&mq->es, total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size))) {
+ DMERR("couldn't initialize entry space");
+ goto bad_pool_init;
+ }
+
+ init_allocator(&mq->writeback_sentinel_alloc, &mq->es, 0, nr_sentinels_per_queue);
+ for (i = 0; i < nr_sentinels_per_queue; i++)
+ get_entry(&mq->writeback_sentinel_alloc, i)->sentinel = true;
+
+ init_allocator(&mq->demote_sentinel_alloc, &mq->es, nr_sentinels_per_queue, total_sentinels);
+ for (i = 0; i < nr_sentinels_per_queue; i++)
+ get_entry(&mq->demote_sentinel_alloc, i)->sentinel = true;
+
+ init_allocator(&mq->hotspot_alloc, &mq->es, total_sentinels,
+ total_sentinels + mq->nr_hotspot_blocks);
+
+ init_allocator(&mq->cache_alloc, &mq->es,
+ total_sentinels + mq->nr_hotspot_blocks,
+ total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size));
+
+ mq->hotspot_hit_bits = alloc_bitset(mq->nr_hotspot_blocks);
+ if (!mq->hotspot_hit_bits) {
+ DMERR("couldn't allocate hotspot hit bitset");
+ goto bad_hotspot_hit_bits;
+ }
+ clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks);
+
+ if (from_cblock(cache_size)) {
+ mq->cache_hit_bits = alloc_bitset(from_cblock(cache_size));
+ if (!mq->cache_hit_bits && mq->cache_hit_bits) {
+ DMERR("couldn't allocate cache hit bitset");
+ goto bad_cache_hit_bits;
+ }
+ clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size));
+ } else
+ mq->cache_hit_bits = NULL;
+
+ mq->tick_protected = 0;
+ mq->tick = 0;
+ mutex_init(&mq->lock);
+ spin_lock_init(&mq->tick_lock);
+
+ q_init(&mq->hotspot, &mq->es, NR_HOTSPOT_LEVELS);
+ mq->hotspot.nr_top_levels = 8;
+ mq->hotspot.nr_in_top_levels = min(mq->nr_hotspot_blocks / NR_HOTSPOT_LEVELS,
+ from_cblock(mq->cache_size) / mq->cache_blocks_per_hotspot_block);
+
+ q_init(&mq->clean, &mq->es, NR_CACHE_LEVELS);
+ q_init(&mq->dirty, &mq->es, NR_CACHE_LEVELS);
+
+ stats_init(&mq->hotspot_stats, NR_HOTSPOT_LEVELS);
+ stats_init(&mq->cache_stats, NR_CACHE_LEVELS);
+
+ if (h_init(&mq->table, &mq->es, from_cblock(cache_size)))
+ goto bad_alloc_table;
+
+ if (h_init(&mq->hotspot_table, &mq->es, mq->nr_hotspot_blocks))
+ goto bad_alloc_hotspot_table;
+
+ sentinels_init(mq);
+ mq->write_promote_level = mq->read_promote_level = NR_HOTSPOT_LEVELS;
+
+ mq->next_hotspot_period = jiffies;
+ mq->next_cache_period = jiffies;
+
+ return &mq->policy;
+
+bad_alloc_hotspot_table:
+ h_exit(&mq->table);
+bad_alloc_table:
+ free_bitset(mq->cache_hit_bits);
+bad_cache_hit_bits:
+ free_bitset(mq->hotspot_hit_bits);
+bad_hotspot_hit_bits:
+ space_exit(&mq->es);
+bad_pool_init:
+ kfree(mq);
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------*/
+
+static struct dm_cache_policy_type smq_policy_type = {
+ .name = "smq",
+ .version = {1, 0, 0},
+ .hint_size = 4,
+ .owner = THIS_MODULE,
+ .create = smq_create
+};
+
+static struct dm_cache_policy_type default_policy_type = {
+ .name = "default",
+ .version = {1, 0, 0},
+ .hint_size = 4,
+ .owner = THIS_MODULE,
+ .create = smq_create,
+ .real = &smq_policy_type
+};
+
+static int __init smq_init(void)
+{
+ int r;
+
+ r = dm_cache_policy_register(&smq_policy_type);
+ if (r) {
+ DMERR("register failed %d", r);
+ return -ENOMEM;
+ }
+
+ r = dm_cache_policy_register(&default_policy_type);
+ if (r) {
+ DMERR("register failed (as default) %d", r);
+ dm_cache_policy_unregister(&smq_policy_type);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void __exit smq_exit(void)
+{
+ dm_cache_policy_unregister(&smq_policy_type);
+ dm_cache_policy_unregister(&default_policy_type);
+}
+
+module_init(smq_init);
+module_exit(smq_exit);
+
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("smq cache policy");
diff --git a/drivers/md/dm-cache-policy.h b/drivers/md/dm-cache-policy.h
index f50fe36..05db56e 100644
--- a/drivers/md/dm-cache-policy.h
+++ b/drivers/md/dm-cache-policy.h
@@ -70,6 +70,18 @@ enum policy_operation {
};
/*
+ * When issuing a POLICY_REPLACE the policy needs to make a callback to
+ * lock the block being demoted. This doesn't need to occur during a
+ * writeback operation since the block remains in the cache.
+ */
+struct policy_locker;
+typedef int (*policy_lock_fn)(struct policy_locker *l, dm_oblock_t oblock);
+
+struct policy_locker {
+ policy_lock_fn fn;
+};
+
+/*
* This is the instruction passed back to the core target.
*/
struct policy_result {
@@ -122,7 +134,8 @@ struct dm_cache_policy {
*/
int (*map)(struct dm_cache_policy *p, dm_oblock_t oblock,
bool can_block, bool can_migrate, bool discarded_oblock,
- struct bio *bio, struct policy_result *result);
+ struct bio *bio, struct policy_locker *locker,
+ struct policy_result *result);
/*
* Sometimes we want to see if a block is in the cache, without
@@ -165,7 +178,9 @@ struct dm_cache_policy {
int (*remove_cblock)(struct dm_cache_policy *p, dm_cblock_t cblock);
/*
- * Provide a dirty block to be written back by the core target.
+ * Provide a dirty block to be written back by the core target. If
+ * critical_only is set then the policy should only provide work if
+ * it urgently needs it.
*
* Returns:
*
@@ -173,7 +188,8 @@ struct dm_cache_policy {
*
* -ENODATA: no dirty blocks available
*/
- int (*writeback_work)(struct dm_cache_policy *p, dm_oblock_t *oblock, dm_cblock_t *cblock);
+ int (*writeback_work)(struct dm_cache_policy *p, dm_oblock_t *oblock, dm_cblock_t *cblock,
+ bool critical_only);
/*
* How full is the cache?
@@ -184,16 +200,16 @@ struct dm_cache_policy {
* Because of where we sit in the block layer, we can be asked to
* map a lot of little bios that are all in the same block (no
* queue merging has occurred). To stop the policy being fooled by
- * these the core target sends regular tick() calls to the policy.
+ * these, the core target sends regular tick() calls to the policy.
* The policy should only count an entry as hit once per tick.
*/
- void (*tick)(struct dm_cache_policy *p);
+ void (*tick)(struct dm_cache_policy *p, bool can_block);
/*
* Configuration.
*/
- int (*emit_config_values)(struct dm_cache_policy *p,
- char *result, unsigned maxlen);
+ int (*emit_config_values)(struct dm_cache_policy *p, char *result,
+ unsigned maxlen, ssize_t *sz_ptr);
int (*set_config_value)(struct dm_cache_policy *p,
const char *key, const char *value);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 7755af3..1b4e175 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -25,44 +25,93 @@ DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(cache_copy_throttle,
/*----------------------------------------------------------------*/
-/*
- * Glossary:
- *
- * oblock: index of an origin block
- * cblock: index of a cache block
- * promotion: movement of a block from origin to cache
- * demotion: movement of a block from cache to origin
- * migration: movement of a block between the origin and cache device,
- * either direction
- */
+#define IOT_RESOLUTION 4
-/*----------------------------------------------------------------*/
+struct io_tracker {
+ spinlock_t lock;
-static size_t bitset_size_in_bytes(unsigned nr_entries)
+ /*
+ * Sectors of in-flight IO.
+ */
+ sector_t in_flight;
+
+ /*
+ * The time, in jiffies, when this device became idle (if it is
+ * indeed idle).
+ */
+ unsigned long idle_time;
+ unsigned long last_update_time;
+};
+
+static void iot_init(struct io_tracker *iot)
{
- return sizeof(unsigned long) * dm_div_up(nr_entries, BITS_PER_LONG);
+ spin_lock_init(&iot->lock);
+ iot->in_flight = 0ul;
+ iot->idle_time = 0ul;
+ iot->last_update_time = jiffies;
}
-static unsigned long *alloc_bitset(unsigned nr_entries)
+static bool __iot_idle_for(struct io_tracker *iot, unsigned long jifs)
{
- size_t s = bitset_size_in_bytes(nr_entries);
- return vzalloc(s);
+ if (iot->in_flight)
+ return false;
+
+ return time_after(jiffies, iot->idle_time + jifs);
}
-static void clear_bitset(void *bitset, unsigned nr_entries)
+static bool iot_idle_for(struct io_tracker *iot, unsigned long jifs)
{
- size_t s = bitset_size_in_bytes(nr_entries);
- memset(bitset, 0, s);
+ bool r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iot->lock, flags);
+ r = __iot_idle_for(iot, jifs);
+ spin_unlock_irqrestore(&iot->lock, flags);
+
+ return r;
}
-static void free_bitset(unsigned long *bits)
+static void iot_io_begin(struct io_tracker *iot, sector_t len)
{
- vfree(bits);
+ unsigned long flags;
+
+ spin_lock_irqsave(&iot->lock, flags);
+ iot->in_flight += len;
+ spin_unlock_irqrestore(&iot->lock, flags);
+}
+
+static void __iot_io_end(struct io_tracker *iot, sector_t len)
+{
+ iot->in_flight -= len;
+ if (!iot->in_flight)
+ iot->idle_time = jiffies;
+}
+
+static void iot_io_end(struct io_tracker *iot, sector_t len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&iot->lock, flags);
+ __iot_io_end(iot, len);
+ spin_unlock_irqrestore(&iot->lock, flags);
}
/*----------------------------------------------------------------*/
/*
+ * Glossary:
+ *
+ * oblock: index of an origin block
+ * cblock: index of a cache block
+ * promotion: movement of a block from origin to cache
+ * demotion: movement of a block from cache to origin
+ * migration: movement of a block between the origin and cache device,
+ * either direction
+ */
+
+/*----------------------------------------------------------------*/
+
+/*
* There are a couple of places where we let a bio run, but want to do some
* work before calling its endio function. We do this by temporarily
* changing the endio fn.
@@ -86,12 +135,6 @@ static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio)
{
bio->bi_end_io = h->bi_end_io;
bio->bi_private = h->bi_private;
-
- /*
- * Must bump bi_remaining to allow bio to complete with
- * restored bi_end_io.
- */
- atomic_inc(&bio->bi_remaining);
}
/*----------------------------------------------------------------*/
@@ -107,12 +150,10 @@ static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio)
#define DATA_DEV_BLOCK_SIZE_MIN_SECTORS (32 * 1024 >> SECTOR_SHIFT)
#define DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
-/*
- * FIXME: the cache is read/write for the time being.
- */
enum cache_metadata_mode {
CM_WRITE, /* metadata may be changed */
CM_READ_ONLY, /* metadata may not be changed */
+ CM_FAIL
};
enum cache_io_mode {
@@ -214,6 +255,7 @@ struct cache {
int sectors_per_block_shift;
spinlock_t lock;
+ struct list_head deferred_cells;
struct bio_list deferred_bios;
struct bio_list deferred_flush_bios;
struct bio_list deferred_writethrough_bios;
@@ -288,6 +330,8 @@ struct cache {
*/
spinlock_t invalidation_lock;
struct list_head invalidation_requests;
+
+ struct io_tracker origin_tracker;
};
struct per_bio_data {
@@ -295,6 +339,7 @@ struct per_bio_data {
unsigned req_nr:2;
struct dm_deferred_entry *all_io_entry;
struct dm_hook_info hook_info;
+ sector_t len;
/*
* writethrough fields. These MUST remain at the end of this
@@ -338,6 +383,8 @@ struct prealloc {
struct dm_bio_prison_cell *cell2;
};
+static enum cache_metadata_mode get_cache_mode(struct cache *cache);
+
static void wake_worker(struct cache *cache)
{
queue_work(cache->wq, &cache->worker);
@@ -371,10 +418,13 @@ static struct dm_cache_migration *alloc_migration(struct cache *cache)
static void free_migration(struct dm_cache_migration *mg)
{
- if (atomic_dec_and_test(&mg->cache->nr_allocated_migrations))
- wake_up(&mg->cache->migration_wait);
+ struct cache *cache = mg->cache;
+
+ if (atomic_dec_and_test(&cache->nr_allocated_migrations))
+ wake_up(&cache->migration_wait);
- mempool_free(mg, mg->cache->migration_pool);
+ mempool_free(mg, cache->migration_pool);
+ wake_worker(cache);
}
static int prealloc_data_structs(struct cache *cache, struct prealloc *p)
@@ -649,6 +699,9 @@ static void save_stats(struct cache *cache)
{
struct dm_cache_statistics stats;
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return;
+
stats.read_hits = atomic_read(&cache->stats.read_hit);
stats.read_misses = atomic_read(&cache->stats.read_miss);
stats.write_hits = atomic_read(&cache->stats.write_hit);
@@ -701,6 +754,7 @@ static struct per_bio_data *init_per_bio_data(struct bio *bio, size_t data_size)
pb->tick = false;
pb->req_nr = dm_bio_get_target_bio_nr(bio);
pb->all_io_entry = NULL;
+ pb->len = 0;
return pb;
}
@@ -798,12 +852,43 @@ static void inc_ds(struct cache *cache, struct bio *bio,
pb->all_io_entry = dm_deferred_entry_inc(cache->all_io_ds);
}
+static bool accountable_bio(struct cache *cache, struct bio *bio)
+{
+ return ((bio->bi_bdev == cache->origin_dev->bdev) &&
+ !(bio->bi_rw & REQ_DISCARD));
+}
+
+static void accounted_begin(struct cache *cache, struct bio *bio)
+{
+ size_t pb_data_size = get_per_bio_data_size(cache);
+ struct per_bio_data *pb = get_per_bio_data(bio, pb_data_size);
+
+ if (accountable_bio(cache, bio)) {
+ pb->len = bio_sectors(bio);
+ iot_io_begin(&cache->origin_tracker, pb->len);
+ }
+}
+
+static void accounted_complete(struct cache *cache, struct bio *bio)
+{
+ size_t pb_data_size = get_per_bio_data_size(cache);
+ struct per_bio_data *pb = get_per_bio_data(bio, pb_data_size);
+
+ iot_io_end(&cache->origin_tracker, pb->len);
+}
+
+static void accounted_request(struct cache *cache, struct bio *bio)
+{
+ accounted_begin(cache, bio);
+ generic_make_request(bio);
+}
+
static void issue(struct cache *cache, struct bio *bio)
{
unsigned long flags;
if (!bio_triggers_commit(cache, bio)) {
- generic_make_request(bio);
+ accounted_request(cache, bio);
return;
}
@@ -876,6 +961,94 @@ static void remap_to_origin_then_cache(struct cache *cache, struct bio *bio,
}
/*----------------------------------------------------------------
+ * Failure modes
+ *--------------------------------------------------------------*/
+static enum cache_metadata_mode get_cache_mode(struct cache *cache)
+{
+ return cache->features.mode;
+}
+
+static const char *cache_device_name(struct cache *cache)
+{
+ return dm_device_name(dm_table_get_md(cache->ti->table));
+}
+
+static void notify_mode_switch(struct cache *cache, enum cache_metadata_mode mode)
+{
+ const char *descs[] = {
+ "write",
+ "read-only",
+ "fail"
+ };
+
+ dm_table_event(cache->ti->table);
+ DMINFO("%s: switching cache to %s mode",
+ cache_device_name(cache), descs[(int)mode]);
+}
+
+static void set_cache_mode(struct cache *cache, enum cache_metadata_mode new_mode)
+{
+ bool needs_check = dm_cache_metadata_needs_check(cache->cmd);
+ enum cache_metadata_mode old_mode = get_cache_mode(cache);
+
+ if (new_mode == CM_WRITE && needs_check) {
+ DMERR("%s: unable to switch cache to write mode until repaired.",
+ cache_device_name(cache));
+ if (old_mode != new_mode)
+ new_mode = old_mode;
+ else
+ new_mode = CM_READ_ONLY;
+ }
+
+ /* Never move out of fail mode */
+ if (old_mode == CM_FAIL)
+ new_mode = CM_FAIL;
+
+ switch (new_mode) {
+ case CM_FAIL:
+ case CM_READ_ONLY:
+ dm_cache_metadata_set_read_only(cache->cmd);
+ break;
+
+ case CM_WRITE:
+ dm_cache_metadata_set_read_write(cache->cmd);
+ break;
+ }
+
+ cache->features.mode = new_mode;
+
+ if (new_mode != old_mode)
+ notify_mode_switch(cache, new_mode);
+}
+
+static void abort_transaction(struct cache *cache)
+{
+ const char *dev_name = cache_device_name(cache);
+
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return;
+
+ if (dm_cache_metadata_set_needs_check(cache->cmd)) {
+ DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name);
+ set_cache_mode(cache, CM_FAIL);
+ }
+
+ DMERR_LIMIT("%s: aborting current metadata transaction", dev_name);
+ if (dm_cache_metadata_abort(cache->cmd)) {
+ DMERR("%s: failed to abort metadata transaction", dev_name);
+ set_cache_mode(cache, CM_FAIL);
+ }
+}
+
+static void metadata_operation_failed(struct cache *cache, const char *op, int r)
+{
+ DMERR_LIMIT("%s: metadata operation '%s' failed: error = %d",
+ cache_device_name(cache), op, r);
+ abort_transaction(cache);
+ set_cache_mode(cache, CM_READ_ONLY);
+}
+
+/*----------------------------------------------------------------
* Migration processing
*
* Migration covers moving data from the origin device to the cache, or
@@ -891,26 +1064,63 @@ static void dec_io_migrations(struct cache *cache)
atomic_dec(&cache->nr_io_migrations);
}
-static void __cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell,
- bool holder)
+static void __cell_release(struct cache *cache, struct dm_bio_prison_cell *cell,
+ bool holder, struct bio_list *bios)
{
(holder ? dm_cell_release : dm_cell_release_no_holder)
- (cache->prison, cell, &cache->deferred_bios);
+ (cache->prison, cell, bios);
free_prison_cell(cache, cell);
}
-static void cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell,
- bool holder)
+static bool discard_or_flush(struct bio *bio)
+{
+ return bio->bi_rw & (REQ_FLUSH | REQ_FUA | REQ_DISCARD);
+}
+
+static void __cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell)
+{
+ if (discard_or_flush(cell->holder))
+ /*
+ * We have to handle these bios
+ * individually.
+ */
+ __cell_release(cache, cell, true, &cache->deferred_bios);
+
+ else
+ list_add_tail(&cell->user_list, &cache->deferred_cells);
+}
+
+static void cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell, bool holder)
{
unsigned long flags;
+ if (!holder && dm_cell_promote_or_release(cache->prison, cell)) {
+ /*
+ * There was no prisoner to promote to holder, the
+ * cell has been released.
+ */
+ free_prison_cell(cache, cell);
+ return;
+ }
+
spin_lock_irqsave(&cache->lock, flags);
- __cell_defer(cache, cell, holder);
+ __cell_defer(cache, cell);
spin_unlock_irqrestore(&cache->lock, flags);
wake_worker(cache);
}
+static void cell_error_with_code(struct cache *cache, struct dm_bio_prison_cell *cell, int err)
+{
+ dm_cell_error(cache->prison, cell, err);
+ dm_bio_prison_free_cell(cache->prison, cell);
+}
+
+static void cell_requeue(struct cache *cache, struct dm_bio_prison_cell *cell)
+{
+ cell_error_with_code(cache, cell, DM_ENDIO_REQUEUE);
+}
+
static void free_io_migration(struct dm_cache_migration *mg)
{
dec_io_migrations(mg->cache);
@@ -920,21 +1130,22 @@ static void free_io_migration(struct dm_cache_migration *mg)
static void migration_failure(struct dm_cache_migration *mg)
{
struct cache *cache = mg->cache;
+ const char *dev_name = cache_device_name(cache);
if (mg->writeback) {
- DMWARN_LIMIT("writeback failed; couldn't copy block");
+ DMERR_LIMIT("%s: writeback failed; couldn't copy block", dev_name);
set_dirty(cache, mg->old_oblock, mg->cblock);
cell_defer(cache, mg->old_ocell, false);
} else if (mg->demote) {
- DMWARN_LIMIT("demotion failed; couldn't copy block");
+ DMERR_LIMIT("%s: demotion failed; couldn't copy block", dev_name);
policy_force_mapping(cache->policy, mg->new_oblock, mg->old_oblock);
cell_defer(cache, mg->old_ocell, mg->promote ? false : true);
if (mg->promote)
cell_defer(cache, mg->new_ocell, true);
} else {
- DMWARN_LIMIT("promotion failed; couldn't copy block");
+ DMERR_LIMIT("%s: promotion failed; couldn't copy block", dev_name);
policy_remove_mapping(cache->policy, mg->new_oblock);
cell_defer(cache, mg->new_ocell, true);
}
@@ -944,6 +1155,7 @@ static void migration_failure(struct dm_cache_migration *mg)
static void migration_success_pre_commit(struct dm_cache_migration *mg)
{
+ int r;
unsigned long flags;
struct cache *cache = mg->cache;
@@ -954,8 +1166,11 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg)
return;
} else if (mg->demote) {
- if (dm_cache_remove_mapping(cache->cmd, mg->cblock)) {
- DMWARN_LIMIT("demotion failed; couldn't update on disk metadata");
+ r = dm_cache_remove_mapping(cache->cmd, mg->cblock);
+ if (r) {
+ DMERR_LIMIT("%s: demotion failed; couldn't update on disk metadata",
+ cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_remove_mapping", r);
policy_force_mapping(cache->policy, mg->new_oblock,
mg->old_oblock);
if (mg->promote)
@@ -964,8 +1179,11 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg)
return;
}
} else {
- if (dm_cache_insert_mapping(cache->cmd, mg->cblock, mg->new_oblock)) {
- DMWARN_LIMIT("promotion failed; couldn't update on disk metadata");
+ r = dm_cache_insert_mapping(cache->cmd, mg->cblock, mg->new_oblock);
+ if (r) {
+ DMERR_LIMIT("%s: promotion failed; couldn't update on disk metadata",
+ cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_insert_mapping", r);
policy_remove_mapping(cache->policy, mg->new_oblock);
free_io_migration(mg);
return;
@@ -984,7 +1202,8 @@ static void migration_success_post_commit(struct dm_cache_migration *mg)
struct cache *cache = mg->cache;
if (mg->writeback) {
- DMWARN("writeback unexpectedly triggered commit");
+ DMWARN_LIMIT("%s: writeback unexpectedly triggered commit",
+ cache_device_name(cache));
return;
} else if (mg->demote) {
@@ -1060,7 +1279,7 @@ static void issue_copy(struct dm_cache_migration *mg)
}
if (r < 0) {
- DMERR_LIMIT("issuing migration failed");
+ DMERR_LIMIT("%s: issuing migration failed", cache_device_name(cache));
migration_failure(mg);
}
}
@@ -1099,7 +1318,7 @@ static void issue_overwrite(struct dm_cache_migration *mg, struct bio *bio)
* No need to inc_ds() here, since the cell will be held for the
* duration of the io.
*/
- generic_make_request(bio);
+ accounted_request(mg->cache, bio);
}
static bool bio_writes_complete_block(struct cache *cache, struct bio *bio)
@@ -1445,32 +1664,154 @@ static void inc_miss_counter(struct cache *cache, struct bio *bio)
&cache->stats.read_miss : &cache->stats.write_miss);
}
-static void process_bio(struct cache *cache, struct prealloc *structs,
- struct bio *bio)
+/*----------------------------------------------------------------*/
+
+struct inc_detail {
+ struct cache *cache;
+ struct bio_list bios_for_issue;
+ struct bio_list unhandled_bios;
+ bool any_writes;
+};
+
+static void inc_fn(void *context, struct dm_bio_prison_cell *cell)
+{
+ struct bio *bio;
+ struct inc_detail *detail = context;
+ struct cache *cache = detail->cache;
+
+ inc_ds(cache, cell->holder, cell);
+ if (bio_data_dir(cell->holder) == WRITE)
+ detail->any_writes = true;
+
+ while ((bio = bio_list_pop(&cell->bios))) {
+ if (discard_or_flush(bio)) {
+ bio_list_add(&detail->unhandled_bios, bio);
+ continue;
+ }
+
+ if (bio_data_dir(bio) == WRITE)
+ detail->any_writes = true;
+
+ bio_list_add(&detail->bios_for_issue, bio);
+ inc_ds(cache, bio, cell);
+ }
+}
+
+// FIXME: refactor these two
+static void remap_cell_to_origin_clear_discard(struct cache *cache,
+ struct dm_bio_prison_cell *cell,
+ dm_oblock_t oblock, bool issue_holder)
+{
+ struct bio *bio;
+ unsigned long flags;
+ struct inc_detail detail;
+
+ detail.cache = cache;
+ bio_list_init(&detail.bios_for_issue);
+ bio_list_init(&detail.unhandled_bios);
+ detail.any_writes = false;
+
+ spin_lock_irqsave(&cache->lock, flags);
+ dm_cell_visit_release(cache->prison, inc_fn, &detail, cell);
+ bio_list_merge(&cache->deferred_bios, &detail.unhandled_bios);
+ spin_unlock_irqrestore(&cache->lock, flags);
+
+ remap_to_origin(cache, cell->holder);
+ if (issue_holder)
+ issue(cache, cell->holder);
+ else
+ accounted_begin(cache, cell->holder);
+
+ if (detail.any_writes)
+ clear_discard(cache, oblock_to_dblock(cache, oblock));
+
+ while ((bio = bio_list_pop(&detail.bios_for_issue))) {
+ remap_to_origin(cache, bio);
+ issue(cache, bio);
+ }
+}
+
+static void remap_cell_to_cache_dirty(struct cache *cache, struct dm_bio_prison_cell *cell,
+ dm_oblock_t oblock, dm_cblock_t cblock, bool issue_holder)
+{
+ struct bio *bio;
+ unsigned long flags;
+ struct inc_detail detail;
+
+ detail.cache = cache;
+ bio_list_init(&detail.bios_for_issue);
+ bio_list_init(&detail.unhandled_bios);
+ detail.any_writes = false;
+
+ spin_lock_irqsave(&cache->lock, flags);
+ dm_cell_visit_release(cache->prison, inc_fn, &detail, cell);
+ bio_list_merge(&cache->deferred_bios, &detail.unhandled_bios);
+ spin_unlock_irqrestore(&cache->lock, flags);
+
+ remap_to_cache(cache, cell->holder, cblock);
+ if (issue_holder)
+ issue(cache, cell->holder);
+ else
+ accounted_begin(cache, cell->holder);
+
+ if (detail.any_writes) {
+ set_dirty(cache, oblock, cblock);
+ clear_discard(cache, oblock_to_dblock(cache, oblock));
+ }
+
+ while ((bio = bio_list_pop(&detail.bios_for_issue))) {
+ remap_to_cache(cache, bio, cblock);
+ issue(cache, bio);
+ }
+}
+
+/*----------------------------------------------------------------*/
+
+struct old_oblock_lock {
+ struct policy_locker locker;
+ struct cache *cache;
+ struct prealloc *structs;
+ struct dm_bio_prison_cell *cell;
+};
+
+static int null_locker(struct policy_locker *locker, dm_oblock_t b)
+{
+ /* This should never be called */
+ BUG();
+ return 0;
+}
+
+static int cell_locker(struct policy_locker *locker, dm_oblock_t b)
+{
+ struct old_oblock_lock *l = container_of(locker, struct old_oblock_lock, locker);
+ struct dm_bio_prison_cell *cell_prealloc = prealloc_get_cell(l->structs);
+
+ return bio_detain(l->cache, b, NULL, cell_prealloc,
+ (cell_free_fn) prealloc_put_cell,
+ l->structs, &l->cell);
+}
+
+static void process_cell(struct cache *cache, struct prealloc *structs,
+ struct dm_bio_prison_cell *new_ocell)
{
int r;
bool release_cell = true;
+ struct bio *bio = new_ocell->holder;
dm_oblock_t block = get_bio_block(cache, bio);
- struct dm_bio_prison_cell *cell_prealloc, *old_ocell, *new_ocell;
struct policy_result lookup_result;
bool passthrough = passthrough_mode(&cache->features);
- bool discarded_block, can_migrate;
-
- /*
- * Check to see if that block is currently migrating.
- */
- cell_prealloc = prealloc_get_cell(structs);
- r = bio_detain(cache, block, bio, cell_prealloc,
- (cell_free_fn) prealloc_put_cell,
- structs, &new_ocell);
- if (r > 0)
- return;
+ bool fast_promotion, can_migrate;
+ struct old_oblock_lock ool;
- discarded_block = is_discarded_oblock(cache, block);
- can_migrate = !passthrough && (discarded_block || spare_migration_bandwidth(cache));
+ fast_promotion = is_discarded_oblock(cache, block) || bio_writes_complete_block(cache, bio);
+ can_migrate = !passthrough && (fast_promotion || spare_migration_bandwidth(cache));
- r = policy_map(cache->policy, block, true, can_migrate, discarded_block,
- bio, &lookup_result);
+ ool.locker.fn = cell_locker;
+ ool.cache = cache;
+ ool.structs = structs;
+ ool.cell = NULL;
+ r = policy_map(cache->policy, block, true, can_migrate, fast_promotion,
+ bio, &ool.locker, &lookup_result);
if (r == -EWOULDBLOCK)
/* migration has been denied */
@@ -1506,9 +1847,9 @@ static void process_bio(struct cache *cache, struct prealloc *structs,
remap_to_origin_then_cache(cache, bio, block, lookup_result.cblock);
inc_and_issue(cache, bio, new_ocell);
- } else {
- remap_to_cache_dirty(cache, bio, block, lookup_result.cblock);
- inc_and_issue(cache, bio, new_ocell);
+ } else {
+ remap_cell_to_cache_dirty(cache, new_ocell, block, lookup_result.cblock, true);
+ release_cell = false;
}
}
@@ -1516,8 +1857,8 @@ static void process_bio(struct cache *cache, struct prealloc *structs,
case POLICY_MISS:
inc_miss_counter(cache, bio);
- remap_to_origin_clear_discard(cache, bio, block);
- inc_and_issue(cache, bio, new_ocell);
+ remap_cell_to_origin_clear_discard(cache, new_ocell, block, true);
+ release_cell = false;
break;
case POLICY_NEW:
@@ -1527,32 +1868,17 @@ static void process_bio(struct cache *cache, struct prealloc *structs,
break;
case POLICY_REPLACE:
- cell_prealloc = prealloc_get_cell(structs);
- r = bio_detain(cache, lookup_result.old_oblock, bio, cell_prealloc,
- (cell_free_fn) prealloc_put_cell,
- structs, &old_ocell);
- if (r > 0) {
- /*
- * We have to be careful to avoid lock inversion of
- * the cells. So we back off, and wait for the
- * old_ocell to become free.
- */
- policy_force_mapping(cache->policy, block,
- lookup_result.old_oblock);
- atomic_inc(&cache->stats.cache_cell_clash);
- break;
- }
atomic_inc(&cache->stats.demotion);
atomic_inc(&cache->stats.promotion);
-
demote_then_promote(cache, structs, lookup_result.old_oblock,
block, lookup_result.cblock,
- old_ocell, new_ocell);
+ ool.cell, new_ocell);
release_cell = false;
break;
default:
- DMERR_LIMIT("%s: erroring bio, unknown policy op: %u", __func__,
+ DMERR_LIMIT("%s: %s: erroring bio, unknown policy op: %u",
+ cache_device_name(cache), __func__,
(unsigned) lookup_result.op);
bio_io_error(bio);
}
@@ -1561,10 +1887,48 @@ static void process_bio(struct cache *cache, struct prealloc *structs,
cell_defer(cache, new_ocell, false);
}
+static void process_bio(struct cache *cache, struct prealloc *structs,
+ struct bio *bio)
+{
+ int r;
+ dm_oblock_t block = get_bio_block(cache, bio);
+ struct dm_bio_prison_cell *cell_prealloc, *new_ocell;
+
+ /*
+ * Check to see if that block is currently migrating.
+ */
+ cell_prealloc = prealloc_get_cell(structs);
+ r = bio_detain(cache, block, bio, cell_prealloc,
+ (cell_free_fn) prealloc_put_cell,
+ structs, &new_ocell);
+ if (r > 0)
+ return;
+
+ process_cell(cache, structs, new_ocell);
+}
+
static int need_commit_due_to_time(struct cache *cache)
{
- return !time_in_range(jiffies, cache->last_commit_jiffies,
- cache->last_commit_jiffies + COMMIT_PERIOD);
+ return jiffies < cache->last_commit_jiffies ||
+ jiffies > cache->last_commit_jiffies + COMMIT_PERIOD;
+}
+
+/*
+ * A non-zero return indicates read_only or fail_io mode.
+ */
+static int commit(struct cache *cache, bool clean_shutdown)
+{
+ int r;
+
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return -EINVAL;
+
+ atomic_inc(&cache->stats.commit_count);
+ r = dm_cache_commit(cache->cmd, clean_shutdown);
+ if (r)
+ metadata_operation_failed(cache, "dm_cache_commit", r);
+
+ return r;
}
static int commit_if_needed(struct cache *cache)
@@ -1573,9 +1937,8 @@ static int commit_if_needed(struct cache *cache)
if ((cache->commit_requested || need_commit_due_to_time(cache)) &&
dm_cache_changed_this_transaction(cache->cmd)) {
- atomic_inc(&cache->stats.commit_count);
+ r = commit(cache, false);
cache->commit_requested = false;
- r = dm_cache_commit(cache->cmd, false);
cache->last_commit_jiffies = jiffies;
}
@@ -1623,6 +1986,40 @@ static void process_deferred_bios(struct cache *cache)
prealloc_free_structs(cache, &structs);
}
+static void process_deferred_cells(struct cache *cache)
+{
+ unsigned long flags;
+ struct dm_bio_prison_cell *cell, *tmp;
+ struct list_head cells;
+ struct prealloc structs;
+
+ memset(&structs, 0, sizeof(structs));
+
+ INIT_LIST_HEAD(&cells);
+
+ spin_lock_irqsave(&cache->lock, flags);
+ list_splice_init(&cache->deferred_cells, &cells);
+ spin_unlock_irqrestore(&cache->lock, flags);
+
+ list_for_each_entry_safe(cell, tmp, &cells, user_list) {
+ /*
+ * If we've got no free migration structs, and processing
+ * this bio might require one, we pause until there are some
+ * prepared mappings to process.
+ */
+ if (prealloc_data_structs(cache, &structs)) {
+ spin_lock_irqsave(&cache->lock, flags);
+ list_splice(&cells, &cache->deferred_cells);
+ spin_unlock_irqrestore(&cache->lock, flags);
+ break;
+ }
+
+ process_cell(cache, &structs, cell);
+ }
+
+ prealloc_free_structs(cache, &structs);
+}
+
static void process_deferred_flush_bios(struct cache *cache, bool submit_bios)
{
unsigned long flags;
@@ -1640,7 +2037,7 @@ static void process_deferred_flush_bios(struct cache *cache, bool submit_bios)
* These bios have already been through inc_ds()
*/
while ((bio = bio_list_pop(&bios)))
- submit_bios ? generic_make_request(bio) : bio_io_error(bio);
+ submit_bios ? accounted_request(cache, bio) : bio_io_error(bio);
}
static void process_deferred_writethrough_bios(struct cache *cache)
@@ -1660,7 +2057,7 @@ static void process_deferred_writethrough_bios(struct cache *cache)
* These bios have already been through inc_ds()
*/
while ((bio = bio_list_pop(&bios)))
- generic_make_request(bio);
+ accounted_request(cache, bio);
}
static void writeback_some_dirty_blocks(struct cache *cache)
@@ -1670,6 +2067,7 @@ static void writeback_some_dirty_blocks(struct cache *cache)
dm_cblock_t cblock;
struct prealloc structs;
struct dm_bio_prison_cell *old_ocell;
+ bool busy = !iot_idle_for(&cache->origin_tracker, HZ);
memset(&structs, 0, sizeof(structs));
@@ -1677,7 +2075,7 @@ static void writeback_some_dirty_blocks(struct cache *cache)
if (prealloc_data_structs(cache, &structs))
break;
- r = policy_writeback_work(cache->policy, &oblock, &cblock);
+ r = policy_writeback_work(cache->policy, &oblock, &cblock, busy);
if (r)
break;
@@ -1708,15 +2106,17 @@ static void process_invalidation_request(struct cache *cache, struct invalidatio
r = policy_remove_cblock(cache->policy, to_cblock(begin));
if (!r) {
r = dm_cache_remove_mapping(cache->cmd, to_cblock(begin));
- if (r)
+ if (r) {
+ metadata_operation_failed(cache, "dm_cache_remove_mapping", r);
break;
+ }
} else if (r == -ENODATA) {
/* harmless, already unmapped */
r = 0;
} else {
- DMERR("policy_remove_cblock failed");
+ DMERR("%s: policy_remove_cblock failed", cache_device_name(cache));
break;
}
@@ -1789,7 +2189,22 @@ static void stop_worker(struct cache *cache)
flush_workqueue(cache->wq);
}
-static void requeue_deferred_io(struct cache *cache)
+static void requeue_deferred_cells(struct cache *cache)
+{
+ unsigned long flags;
+ struct list_head cells;
+ struct dm_bio_prison_cell *cell, *tmp;
+
+ INIT_LIST_HEAD(&cells);
+ spin_lock_irqsave(&cache->lock, flags);
+ list_splice_init(&cache->deferred_cells, &cells);
+ spin_unlock_irqrestore(&cache->lock, flags);
+
+ list_for_each_entry_safe(cell, tmp, &cells, user_list)
+ cell_requeue(cache, cell);
+}
+
+static void requeue_deferred_bios(struct cache *cache)
{
struct bio *bio;
struct bio_list bios;
@@ -1810,6 +2225,7 @@ static int more_work(struct cache *cache)
!list_empty(&cache->need_commit_migrations);
else
return !bio_list_empty(&cache->deferred_bios) ||
+ !list_empty(&cache->deferred_cells) ||
!bio_list_empty(&cache->deferred_flush_bios) ||
!bio_list_empty(&cache->deferred_writethrough_bios) ||
!list_empty(&cache->quiesced_migrations) ||
@@ -1827,6 +2243,7 @@ static void do_worker(struct work_struct *ws)
writeback_some_dirty_blocks(cache);
process_deferred_writethrough_bios(cache);
process_deferred_bios(cache);
+ process_deferred_cells(cache);
process_invalidation_requests(cache);
}
@@ -1836,11 +2253,6 @@ static void do_worker(struct work_struct *ws)
if (commit_if_needed(cache)) {
process_deferred_flush_bios(cache, false);
process_migrations(cache, &cache->need_commit_migrations, migration_failure);
-
- /*
- * FIXME: rollback metadata or just go into a
- * failure mode and error everything
- */
} else {
process_deferred_flush_bios(cache, true);
process_migrations(cache, &cache->need_commit_migrations,
@@ -1859,7 +2271,7 @@ static void do_worker(struct work_struct *ws)
static void do_waker(struct work_struct *ws)
{
struct cache *cache = container_of(to_delayed_work(ws), struct cache, waker);
- policy_tick(cache->policy);
+ policy_tick(cache->policy, true);
wake_worker(cache);
queue_delayed_work(cache->wq, &cache->waker, COMMIT_PERIOD);
}
@@ -2413,6 +2825,12 @@ static int cache_create(struct cache_args *ca, struct cache **result)
goto bad;
}
cache->cmd = cmd;
+ set_cache_mode(cache, CM_WRITE);
+ if (get_cache_mode(cache) != CM_WRITE) {
+ *error = "Unable to get write access to metadata, please check/repair metadata.";
+ r = -EINVAL;
+ goto bad;
+ }
if (passthrough_mode(&cache->features)) {
bool all_clean;
@@ -2431,6 +2849,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)
}
spin_lock_init(&cache->lock);
+ INIT_LIST_HEAD(&cache->deferred_cells);
bio_list_init(&cache->deferred_bios);
bio_list_init(&cache->deferred_flush_bios);
bio_list_init(&cache->deferred_writethrough_bios);
@@ -2520,6 +2939,8 @@ static int cache_create(struct cache_args *ca, struct cache **result)
spin_lock_init(&cache->invalidation_lock);
INIT_LIST_HEAD(&cache->invalidation_requests);
+ iot_init(&cache->origin_tracker);
+
*result = cache;
return 0;
@@ -2586,15 +3007,23 @@ out:
return r;
}
-static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_prison_cell **cell)
+/*----------------------------------------------------------------*/
+
+static int cache_map(struct dm_target *ti, struct bio *bio)
{
+ struct cache *cache = ti->private;
+
int r;
+ struct dm_bio_prison_cell *cell = NULL;
dm_oblock_t block = get_bio_block(cache, bio);
size_t pb_data_size = get_per_bio_data_size(cache);
bool can_migrate = false;
- bool discarded_block;
+ bool fast_promotion;
struct policy_result lookup_result;
struct per_bio_data *pb = init_per_bio_data(bio, pb_data_size);
+ struct old_oblock_lock ool;
+
+ ool.locker.fn = null_locker;
if (unlikely(from_oblock(block) >= from_oblock(cache->origin_blocks))) {
/*
@@ -2603,10 +3032,11 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
* Just remap to the origin and carry on.
*/
remap_to_origin(cache, bio);
+ accounted_begin(cache, bio);
return DM_MAPIO_REMAPPED;
}
- if (bio->bi_rw & (REQ_FLUSH | REQ_FUA | REQ_DISCARD)) {
+ if (discard_or_flush(bio)) {
defer_bio(cache, bio);
return DM_MAPIO_SUBMITTED;
}
@@ -2614,15 +3044,15 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
/*
* Check to see if that block is currently migrating.
*/
- *cell = alloc_prison_cell(cache);
- if (!*cell) {
+ cell = alloc_prison_cell(cache);
+ if (!cell) {
defer_bio(cache, bio);
return DM_MAPIO_SUBMITTED;
}
- r = bio_detain(cache, block, bio, *cell,
+ r = bio_detain(cache, block, bio, cell,
(cell_free_fn) free_prison_cell,
- cache, cell);
+ cache, &cell);
if (r) {
if (r < 0)
defer_bio(cache, bio);
@@ -2630,17 +3060,18 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
return DM_MAPIO_SUBMITTED;
}
- discarded_block = is_discarded_oblock(cache, block);
+ fast_promotion = is_discarded_oblock(cache, block) || bio_writes_complete_block(cache, bio);
- r = policy_map(cache->policy, block, false, can_migrate, discarded_block,
- bio, &lookup_result);
+ r = policy_map(cache->policy, block, false, can_migrate, fast_promotion,
+ bio, &ool.locker, &lookup_result);
if (r == -EWOULDBLOCK) {
- cell_defer(cache, *cell, true);
+ cell_defer(cache, cell, true);
return DM_MAPIO_SUBMITTED;
} else if (r) {
- DMERR_LIMIT("Unexpected return from cache replacement policy: %d", r);
- cell_defer(cache, *cell, false);
+ DMERR_LIMIT("%s: Unexpected return from cache replacement policy: %d",
+ cache_device_name(cache), r);
+ cell_defer(cache, cell, false);
bio_io_error(bio);
return DM_MAPIO_SUBMITTED;
}
@@ -2654,21 +3085,30 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
* We need to invalidate this block, so
* defer for the worker thread.
*/
- cell_defer(cache, *cell, true);
+ cell_defer(cache, cell, true);
r = DM_MAPIO_SUBMITTED;
} else {
inc_miss_counter(cache, bio);
remap_to_origin_clear_discard(cache, bio, block);
+ accounted_begin(cache, bio);
+ inc_ds(cache, bio, cell);
+ // FIXME: we want to remap hits or misses straight
+ // away rather than passing over to the worker.
+ cell_defer(cache, cell, false);
}
} else {
inc_hit_counter(cache, bio);
if (bio_data_dir(bio) == WRITE && writethrough_mode(&cache->features) &&
- !is_dirty(cache, lookup_result.cblock))
+ !is_dirty(cache, lookup_result.cblock)) {
remap_to_origin_then_cache(cache, bio, block, lookup_result.cblock);
- else
- remap_to_cache_dirty(cache, bio, block, lookup_result.cblock);
+ accounted_begin(cache, bio);
+ inc_ds(cache, bio, cell);
+ cell_defer(cache, cell, false);
+
+ } else
+ remap_cell_to_cache_dirty(cache, cell, block, lookup_result.cblock, false);
}
break;
@@ -2680,18 +3120,19 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
* longer needed because the block has been demoted.
*/
bio_endio(bio, 0);
- cell_defer(cache, *cell, false);
+ // FIXME: remap everything as a miss
+ cell_defer(cache, cell, false);
r = DM_MAPIO_SUBMITTED;
} else
- remap_to_origin_clear_discard(cache, bio, block);
-
+ remap_cell_to_origin_clear_discard(cache, cell, block, false);
break;
default:
- DMERR_LIMIT("%s: erroring bio: unknown policy op: %u", __func__,
+ DMERR_LIMIT("%s: %s: erroring bio: unknown policy op: %u",
+ cache_device_name(cache), __func__,
(unsigned) lookup_result.op);
- cell_defer(cache, *cell, false);
+ cell_defer(cache, cell, false);
bio_io_error(bio);
r = DM_MAPIO_SUBMITTED;
}
@@ -2699,21 +3140,6 @@ static int __cache_map(struct cache *cache, struct bio *bio, struct dm_bio_priso
return r;
}
-static int cache_map(struct dm_target *ti, struct bio *bio)
-{
- int r;
- struct dm_bio_prison_cell *cell = NULL;
- struct cache *cache = ti->private;
-
- r = __cache_map(cache, bio, &cell);
- if (r == DM_MAPIO_REMAPPED && cell) {
- inc_ds(cache, bio, cell);
- cell_defer(cache, cell, false);
- }
-
- return r;
-}
-
static int cache_end_io(struct dm_target *ti, struct bio *bio, int error)
{
struct cache *cache = ti->private;
@@ -2722,7 +3148,7 @@ static int cache_end_io(struct dm_target *ti, struct bio *bio, int error)
struct per_bio_data *pb = get_per_bio_data(bio, pb_data_size);
if (pb->tick) {
- policy_tick(cache->policy);
+ policy_tick(cache->policy, false);
spin_lock_irqsave(&cache->lock, flags);
cache->need_tick_bio = true;
@@ -2730,6 +3156,7 @@ static int cache_end_io(struct dm_target *ti, struct bio *bio, int error)
}
check_for_quiesced_migrations(cache, pb);
+ accounted_complete(cache, bio);
return 0;
}
@@ -2738,11 +3165,16 @@ static int write_dirty_bitset(struct cache *cache)
{
unsigned i, r;
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return -EINVAL;
+
for (i = 0; i < from_cblock(cache->cache_size); i++) {
r = dm_cache_set_dirty(cache->cmd, to_cblock(i),
is_dirty(cache, to_cblock(i)));
- if (r)
+ if (r) {
+ metadata_operation_failed(cache, "dm_cache_set_dirty", r);
return r;
+ }
}
return 0;
@@ -2752,18 +3184,40 @@ static int write_discard_bitset(struct cache *cache)
{
unsigned i, r;
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return -EINVAL;
+
r = dm_cache_discard_bitset_resize(cache->cmd, cache->discard_block_size,
cache->discard_nr_blocks);
if (r) {
- DMERR("could not resize on-disk discard bitset");
+ DMERR("%s: could not resize on-disk discard bitset", cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_discard_bitset_resize", r);
return r;
}
for (i = 0; i < from_dblock(cache->discard_nr_blocks); i++) {
r = dm_cache_set_discard(cache->cmd, to_dblock(i),
is_discarded(cache, to_dblock(i)));
- if (r)
+ if (r) {
+ metadata_operation_failed(cache, "dm_cache_set_discard", r);
return r;
+ }
+ }
+
+ return 0;
+}
+
+static int write_hints(struct cache *cache)
+{
+ int r;
+
+ if (get_cache_mode(cache) >= CM_READ_ONLY)
+ return -EINVAL;
+
+ r = dm_cache_write_hints(cache->cmd, cache->policy);
+ if (r) {
+ metadata_operation_failed(cache, "dm_cache_write_hints", r);
+ return r;
}
return 0;
@@ -2778,26 +3232,26 @@ static bool sync_metadata(struct cache *cache)
r1 = write_dirty_bitset(cache);
if (r1)
- DMERR("could not write dirty bitset");
+ DMERR("%s: could not write dirty bitset", cache_device_name(cache));
r2 = write_discard_bitset(cache);
if (r2)
- DMERR("could not write discard bitset");
+ DMERR("%s: could not write discard bitset", cache_device_name(cache));
save_stats(cache);
- r3 = dm_cache_write_hints(cache->cmd, cache->policy);
+ r3 = write_hints(cache);
if (r3)
- DMERR("could not write hints");
+ DMERR("%s: could not write hints", cache_device_name(cache));
/*
* If writing the above metadata failed, we still commit, but don't
* set the clean shutdown flag. This will effectively force every
* dirty bit to be set on reload.
*/
- r4 = dm_cache_commit(cache->cmd, !r1 && !r2 && !r3);
+ r4 = commit(cache, !r1 && !r2 && !r3);
if (r4)
- DMERR("could not write cache metadata. Data loss may occur.");
+ DMERR("%s: could not write cache metadata", cache_device_name(cache));
return !r1 && !r2 && !r3 && !r4;
}
@@ -2809,10 +3263,12 @@ static void cache_postsuspend(struct dm_target *ti)
start_quiescing(cache);
wait_for_migrations(cache);
stop_worker(cache);
- requeue_deferred_io(cache);
+ requeue_deferred_bios(cache);
+ requeue_deferred_cells(cache);
stop_quiescing(cache);
- (void) sync_metadata(cache);
+ if (get_cache_mode(cache) == CM_WRITE)
+ (void) sync_metadata(cache);
}
static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
@@ -2935,7 +3391,8 @@ static bool can_resize(struct cache *cache, dm_cblock_t new_size)
while (from_cblock(new_size) < from_cblock(cache->cache_size)) {
new_size = to_cblock(from_cblock(new_size) + 1);
if (is_dirty(cache, new_size)) {
- DMERR("unable to shrink cache; cache block %llu is dirty",
+ DMERR("%s: unable to shrink cache; cache block %llu is dirty",
+ cache_device_name(cache),
(unsigned long long) from_cblock(new_size));
return false;
}
@@ -2950,7 +3407,8 @@ static int resize_cache_dev(struct cache *cache, dm_cblock_t new_size)
r = dm_cache_resize(cache->cmd, new_size);
if (r) {
- DMERR("could not resize cache metadata");
+ DMERR("%s: could not resize cache metadata", cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_resize", r);
return r;
}
@@ -2988,7 +3446,8 @@ static int cache_preresume(struct dm_target *ti)
r = dm_cache_load_mappings(cache->cmd, cache->policy,
load_mapping, cache);
if (r) {
- DMERR("could not load cache mappings");
+ DMERR("%s: could not load cache mappings", cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_load_mappings", r);
return r;
}
@@ -3008,7 +3467,8 @@ static int cache_preresume(struct dm_target *ti)
discard_load_info_init(cache, &li);
r = dm_cache_load_discards(cache->cmd, load_discard, &li);
if (r) {
- DMERR("could not load origin discards");
+ DMERR("%s: could not load origin discards", cache_device_name(cache));
+ metadata_operation_failed(cache, "dm_cache_load_discards", r);
return r;
}
set_discard_range(&li);
@@ -3036,7 +3496,7 @@ static void cache_resume(struct dm_target *ti)
* <#demotions> <#promotions> <#dirty>
* <#features> <features>*
* <#core args> <core args>
- * <policy name> <#policy args> <policy args>*
+ * <policy name> <#policy args> <policy args>* <cache metadata mode>
*/
static void cache_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
@@ -3052,23 +3512,26 @@ static void cache_status(struct dm_target *ti, status_type_t type,
switch (type) {
case STATUSTYPE_INFO:
- /* Commit to ensure statistics aren't out-of-date */
- if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti)) {
- r = dm_cache_commit(cache->cmd, false);
- if (r)
- DMERR("could not commit metadata for accurate status");
+ if (get_cache_mode(cache) == CM_FAIL) {
+ DMEMIT("Fail");
+ break;
}
- r = dm_cache_get_free_metadata_block_count(cache->cmd,
- &nr_free_blocks_metadata);
+ /* Commit to ensure statistics aren't out-of-date */
+ if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti))
+ (void) commit(cache, false);
+
+ r = dm_cache_get_free_metadata_block_count(cache->cmd, &nr_free_blocks_metadata);
if (r) {
- DMERR("could not get metadata free block count");
+ DMERR("%s: dm_cache_get_free_metadata_block_count returned %d",
+ cache_device_name(cache), r);
goto err;
}
r = dm_cache_get_metadata_dev_size(cache->cmd, &nr_blocks_metadata);
if (r) {
- DMERR("could not get metadata device size");
+ DMERR("%s: dm_cache_get_metadata_dev_size returned %d",
+ cache_device_name(cache), r);
goto err;
}
@@ -3099,7 +3562,8 @@ static void cache_status(struct dm_target *ti, status_type_t type,
DMEMIT("1 writeback ");
else {
- DMERR("internal error: unknown io mode: %d", (int) cache->features.io_mode);
+ DMERR("%s: internal error: unknown io mode: %d",
+ cache_device_name(cache), (int) cache->features.io_mode);
goto err;
}
@@ -3107,11 +3571,17 @@ static void cache_status(struct dm_target *ti, status_type_t type,
DMEMIT("%s ", dm_cache_policy_get_name(cache->policy));
if (sz < maxlen) {
- r = policy_emit_config_values(cache->policy, result + sz, maxlen - sz);
+ r = policy_emit_config_values(cache->policy, result, maxlen, &sz);
if (r)
- DMERR("policy_emit_config_values returned %d", r);
+ DMERR("%s: policy_emit_config_values returned %d",
+ cache_device_name(cache), r);
}
+ if (get_cache_mode(cache) == CM_READ_ONLY)
+ DMEMIT("ro ");
+ else
+ DMEMIT("rw ");
+
break;
case STATUSTYPE_TABLE:
@@ -3173,7 +3643,7 @@ static int parse_cblock_range(struct cache *cache, const char *str,
return 0;
}
- DMERR("invalid cblock range '%s'", str);
+ DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), str);
return -EINVAL;
}
@@ -3184,17 +3654,20 @@ static int validate_cblock_range(struct cache *cache, struct cblock_range *range
uint64_t n = from_cblock(cache->cache_size);
if (b >= n) {
- DMERR("begin cblock out of range: %llu >= %llu", b, n);
+ DMERR("%s: begin cblock out of range: %llu >= %llu",
+ cache_device_name(cache), b, n);
return -EINVAL;
}
if (e > n) {
- DMERR("end cblock out of range: %llu > %llu", e, n);
+ DMERR("%s: end cblock out of range: %llu > %llu",
+ cache_device_name(cache), e, n);
return -EINVAL;
}
if (b >= e) {
- DMERR("invalid cblock range: %llu >= %llu", b, e);
+ DMERR("%s: invalid cblock range: %llu >= %llu",
+ cache_device_name(cache), b, e);
return -EINVAL;
}
@@ -3228,7 +3701,8 @@ static int process_invalidate_cblocks_message(struct cache *cache, unsigned coun
struct cblock_range range;
if (!passthrough_mode(&cache->features)) {
- DMERR("cache has to be in passthrough mode for invalidation");
+ DMERR("%s: cache has to be in passthrough mode for invalidation",
+ cache_device_name(cache));
return -EPERM;
}
@@ -3267,6 +3741,12 @@ static int cache_message(struct dm_target *ti, unsigned argc, char **argv)
if (!argc)
return -EINVAL;
+ if (get_cache_mode(cache) >= CM_READ_ONLY) {
+ DMERR("%s: unable to service cache target messages in READ_ONLY or FAIL mode",
+ cache_device_name(cache));
+ return -EOPNOTSUPP;
+ }
+
if (!strcasecmp(argv[0], "invalidate_cblocks"))
return process_invalidate_cblocks_message(cache, argc - 1, (const char **) argv + 1);
@@ -3340,7 +3820,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 5503e43..0f48fed 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2003 Jana Saout <jana@saout.de>
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006-2015 Red Hat, Inc. All rights reserved.
* Copyright (C) 2013 Milan Broz <gmazyland@gmail.com>
*
* This file is released under the GPL.
@@ -891,6 +891,11 @@ static void crypt_alloc_req(struct crypt_config *cc,
ctx->req = mempool_alloc(cc->req_pool, GFP_NOIO);
ablkcipher_request_set_tfm(ctx->req, cc->tfms[key_index]);
+
+ /*
+ * Use REQ_MAY_BACKLOG so a cipher driver internally backlogs
+ * requests if driver request queue is full.
+ */
ablkcipher_request_set_callback(ctx->req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
kcryptd_async_done, dmreq_of_req(cc, ctx->req));
@@ -924,24 +929,32 @@ static int crypt_convert(struct crypt_config *cc,
r = crypt_convert_block(cc, ctx, ctx->req);
switch (r) {
- /* async */
+ /*
+ * The request was queued by a crypto driver
+ * but the driver request queue is full, let's wait.
+ */
case -EBUSY:
wait_for_completion(&ctx->restart);
reinit_completion(&ctx->restart);
- /* fall through*/
+ /* fall through */
+ /*
+ * The request is queued and processed asynchronously,
+ * completion function kcryptd_async_done() will be called.
+ */
case -EINPROGRESS:
ctx->req = NULL;
ctx->cc_sector++;
continue;
-
- /* sync */
+ /*
+ * The request was already processed (synchronously).
+ */
case 0:
atomic_dec(&ctx->cc_pending);
ctx->cc_sector++;
cond_resched();
continue;
- /* error */
+ /* There was an error while processing the request. */
default:
atomic_dec(&ctx->cc_pending);
return r;
@@ -1346,6 +1359,11 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
struct crypt_config *cc = io->cc;
+ /*
+ * A request from crypto driver backlog is going to be processed now,
+ * finish the completion and continue in crypt_convert().
+ * (Callback will be called for the second time for this request.)
+ */
if (error == -EINPROGRESS) {
complete(&ctx->restart);
return;
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
index 93e0844..ad1b049 100644
--- a/drivers/md/dm-log-writes.c
+++ b/drivers/md/dm-log-writes.c
@@ -55,8 +55,8 @@
#define LOG_DISCARD_FLAG (1 << 2)
#define LOG_MARK_FLAG (1 << 3)
-#define WRITE_LOG_VERSION 1
-#define WRITE_LOG_MAGIC 0x6a736677736872
+#define WRITE_LOG_VERSION 1ULL
+#define WRITE_LOG_MAGIC 0x6a736677736872ULL
/*
* The disk format for this is braindead simple.
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 88e4c7f..2daa677 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010-2011 Neil Brown
- * Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -17,6 +17,7 @@
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "raid"
+#define MAX_RAID_DEVICES 253 /* raid4/5/6 limit */
static bool devices_handle_discard_safely = false;
@@ -45,25 +46,25 @@ struct raid_dev {
};
/*
- * Flags for rs->print_flags field.
+ * Flags for rs->ctr_flags field.
*/
-#define DMPF_SYNC 0x1
-#define DMPF_NOSYNC 0x2
-#define DMPF_REBUILD 0x4
-#define DMPF_DAEMON_SLEEP 0x8
-#define DMPF_MIN_RECOVERY_RATE 0x10
-#define DMPF_MAX_RECOVERY_RATE 0x20
-#define DMPF_MAX_WRITE_BEHIND 0x40
-#define DMPF_STRIPE_CACHE 0x80
-#define DMPF_REGION_SIZE 0x100
-#define DMPF_RAID10_COPIES 0x200
-#define DMPF_RAID10_FORMAT 0x400
+#define CTR_FLAG_SYNC 0x1
+#define CTR_FLAG_NOSYNC 0x2
+#define CTR_FLAG_REBUILD 0x4
+#define CTR_FLAG_DAEMON_SLEEP 0x8
+#define CTR_FLAG_MIN_RECOVERY_RATE 0x10
+#define CTR_FLAG_MAX_RECOVERY_RATE 0x20
+#define CTR_FLAG_MAX_WRITE_BEHIND 0x40
+#define CTR_FLAG_STRIPE_CACHE 0x80
+#define CTR_FLAG_REGION_SIZE 0x100
+#define CTR_FLAG_RAID10_COPIES 0x200
+#define CTR_FLAG_RAID10_FORMAT 0x400
struct raid_set {
struct dm_target *ti;
uint32_t bitmap_loaded;
- uint32_t print_flags;
+ uint32_t ctr_flags;
struct mddev md;
struct raid_type *raid_type;
@@ -81,6 +82,7 @@ static struct raid_type {
const unsigned level; /* RAID level. */
const unsigned algorithm; /* RAID algorithm. */
} raid_types[] = {
+ {"raid0", "RAID0 (striping)", 0, 2, 0, 0 /* NONE */},
{"raid1", "RAID1 (mirroring)", 0, 2, 1, 0 /* NONE */},
{"raid10", "RAID10 (striped mirrors)", 0, 2, 10, UINT_MAX /* Varies */},
{"raid4", "RAID4 (dedicated parity disk)", 1, 2, 5, ALGORITHM_PARITY_0},
@@ -119,15 +121,15 @@ static int raid10_format_to_md_layout(char *format, unsigned copies)
{
unsigned n = 1, f = 1;
- if (!strcmp("near", format))
+ if (!strcasecmp("near", format))
n = copies;
else
f = copies;
- if (!strcmp("offset", format))
+ if (!strcasecmp("offset", format))
return 0x30000 | (f << 8) | n;
- if (!strcmp("far", format))
+ if (!strcasecmp("far", format))
return 0x20000 | (f << 8) | n;
return (f << 8) | n;
@@ -477,8 +479,6 @@ too_many:
* will form the "stripe"
* [[no]sync] Force or prevent recovery of the
* entire array
- * [devices_handle_discard_safely] Allow discards on RAID4/5/6; useful if RAID
- * member device(s) properly support TRIM/UNMAP
* [rebuild <idx>] Rebuild the drive indicated by the index
* [daemon_sleep <ms>] Time between bitmap daemon work to
* clear bits
@@ -555,12 +555,12 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
for (i = 0; i < num_raid_params; i++) {
if (!strcasecmp(argv[i], "nosync")) {
rs->md.recovery_cp = MaxSector;
- rs->print_flags |= DMPF_NOSYNC;
+ rs->ctr_flags |= CTR_FLAG_NOSYNC;
continue;
}
if (!strcasecmp(argv[i], "sync")) {
rs->md.recovery_cp = 0;
- rs->print_flags |= DMPF_SYNC;
+ rs->ctr_flags |= CTR_FLAG_SYNC;
continue;
}
@@ -585,7 +585,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
}
raid10_format = argv[i];
- rs->print_flags |= DMPF_RAID10_FORMAT;
+ rs->ctr_flags |= CTR_FLAG_RAID10_FORMAT;
continue;
}
@@ -602,7 +602,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
clear_bit(In_sync, &rs->dev[value].rdev.flags);
rs->dev[value].rdev.recovery_offset = 0;
- rs->print_flags |= DMPF_REBUILD;
+ rs->ctr_flags |= CTR_FLAG_REBUILD;
} else if (!strcasecmp(key, "write_mostly")) {
if (rs->raid_type->level != 1) {
rs->ti->error = "write_mostly option is only valid for RAID1";
@@ -618,7 +618,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
rs->ti->error = "max_write_behind option is only valid for RAID1";
return -EINVAL;
}
- rs->print_flags |= DMPF_MAX_WRITE_BEHIND;
+ rs->ctr_flags |= CTR_FLAG_MAX_WRITE_BEHIND;
/*
* In device-mapper, we specify things in sectors, but
@@ -631,14 +631,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
rs->md.bitmap_info.max_write_behind = value;
} else if (!strcasecmp(key, "daemon_sleep")) {
- rs->print_flags |= DMPF_DAEMON_SLEEP;
+ rs->ctr_flags |= CTR_FLAG_DAEMON_SLEEP;
if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
rs->ti->error = "daemon sleep period out of range";
return -EINVAL;
}
rs->md.bitmap_info.daemon_sleep = value;
} else if (!strcasecmp(key, "stripe_cache")) {
- rs->print_flags |= DMPF_STRIPE_CACHE;
+ rs->ctr_flags |= CTR_FLAG_STRIPE_CACHE;
/*
* In device-mapper, we specify things in sectors, but
@@ -656,21 +656,21 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
}
} else if (!strcasecmp(key, "min_recovery_rate")) {
- rs->print_flags |= DMPF_MIN_RECOVERY_RATE;
+ rs->ctr_flags |= CTR_FLAG_MIN_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "min_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_min = (int)value;
} else if (!strcasecmp(key, "max_recovery_rate")) {
- rs->print_flags |= DMPF_MAX_RECOVERY_RATE;
+ rs->ctr_flags |= CTR_FLAG_MAX_RECOVERY_RATE;
if (value > INT_MAX) {
rs->ti->error = "max_recovery_rate out of range";
return -EINVAL;
}
rs->md.sync_speed_max = (int)value;
} else if (!strcasecmp(key, "region_size")) {
- rs->print_flags |= DMPF_REGION_SIZE;
+ rs->ctr_flags |= CTR_FLAG_REGION_SIZE;
region_size = value;
} else if (!strcasecmp(key, "raid10_copies") &&
(rs->raid_type->level == 10)) {
@@ -678,7 +678,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
rs->ti->error = "Bad value for 'raid10_copies'";
return -EINVAL;
}
- rs->print_flags |= DMPF_RAID10_COPIES;
+ rs->ctr_flags |= CTR_FLAG_RAID10_COPIES;
raid10_copies = value;
} else {
DMERR("Unable to parse RAID parameter: %s", key);
@@ -720,7 +720,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
rs->md.layout = raid10_format_to_md_layout(raid10_format,
raid10_copies);
rs->md.new_layout = rs->md.layout;
- } else if ((rs->raid_type->level > 1) &&
+ } else if ((!rs->raid_type->level || rs->raid_type->level > 1) &&
sector_div(sectors_per_dev,
(rs->md.raid_disks - rs->raid_type->parity_devs))) {
rs->ti->error = "Target length not divisible by number of data devices";
@@ -947,7 +947,7 @@ static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
return -EINVAL;
}
- if (!(rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)))
+ if (!(rs->ctr_flags & (CTR_FLAG_SYNC | CTR_FLAG_NOSYNC)))
mddev->recovery_cp = le64_to_cpu(sb->array_resync_offset);
/*
@@ -1026,8 +1026,9 @@ static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
return 0;
}
-static int super_validate(struct mddev *mddev, struct md_rdev *rdev)
+static int super_validate(struct raid_set *rs, struct md_rdev *rdev)
{
+ struct mddev *mddev = &rs->md;
struct dm_raid_superblock *sb = page_address(rdev->sb_page);
/*
@@ -1037,8 +1038,10 @@ static int super_validate(struct mddev *mddev, struct md_rdev *rdev)
if (!mddev->events && super_init_validation(mddev, rdev))
return -EINVAL;
- mddev->bitmap_info.offset = 4096 >> 9; /* Enable bitmap creation */
- rdev->mddev->bitmap_info.default_offset = 4096 >> 9;
+ /* Enable bitmap creation for RAID levels != 0 */
+ mddev->bitmap_info.offset = (rs->raid_type->level) ? to_sector(4096) : 0;
+ rdev->mddev->bitmap_info.default_offset = mddev->bitmap_info.offset;
+
if (!test_bit(FirstUse, &rdev->flags)) {
rdev->recovery_offset = le64_to_cpu(sb->disk_recovery_offset);
if (rdev->recovery_offset != MaxSector)
@@ -1073,7 +1076,7 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
freshest = NULL;
rdev_for_each_safe(rdev, tmp, mddev) {
/*
- * Skipping super_load due to DMPF_SYNC will cause
+ * Skipping super_load due to CTR_FLAG_SYNC will cause
* the array to undergo initialization again as
* though it were new. This is the intended effect
* of the "sync" directive.
@@ -1082,7 +1085,9 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
* that the "sync" directive is disallowed during the
* reshape.
*/
- if (rs->print_flags & DMPF_SYNC)
+ rdev->sectors = to_sector(i_size_read(rdev->bdev->bd_inode));
+
+ if (rs->ctr_flags & CTR_FLAG_SYNC)
continue;
if (!rdev->meta_bdev)
@@ -1140,11 +1145,11 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
* validation for the remaining devices.
*/
ti->error = "Unable to assemble array: Invalid superblocks";
- if (super_validate(mddev, freshest))
+ if (super_validate(rs, freshest))
return -EINVAL;
rdev_for_each(rdev, mddev)
- if ((rdev != freshest) && super_validate(mddev, rdev))
+ if ((rdev != freshest) && super_validate(rs, rdev))
return -EINVAL;
return 0;
@@ -1243,7 +1248,7 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
if ((kstrtoul(argv[num_raid_params], 10, &num_raid_devs) < 0) ||
- (num_raid_devs >= INT_MAX)) {
+ (num_raid_devs > MAX_RAID_DEVICES)) {
ti->error = "Cannot understand number of raid devices";
return -EINVAL;
}
@@ -1282,10 +1287,11 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
*/
configure_discard_support(ti, rs);
- mutex_lock(&rs->md.reconfig_mutex);
+ /* Has to be held on running the array */
+ mddev_lock_nointr(&rs->md);
ret = md_run(&rs->md);
rs->md.in_sync = 0; /* Assume already marked dirty */
- mutex_unlock(&rs->md.reconfig_mutex);
+ mddev_unlock(&rs->md);
if (ret) {
ti->error = "Fail to run raid array";
@@ -1368,34 +1374,40 @@ static void raid_status(struct dm_target *ti, status_type_t type,
case STATUSTYPE_INFO:
DMEMIT("%s %d ", rs->raid_type->name, rs->md.raid_disks);
- if (test_bit(MD_RECOVERY_RUNNING, &rs->md.recovery))
- sync = rs->md.curr_resync_completed;
- else
- sync = rs->md.recovery_cp;
-
- if (sync >= rs->md.resync_max_sectors) {
- /*
- * Sync complete.
- */
+ if (rs->raid_type->level) {
+ if (test_bit(MD_RECOVERY_RUNNING, &rs->md.recovery))
+ sync = rs->md.curr_resync_completed;
+ else
+ sync = rs->md.recovery_cp;
+
+ if (sync >= rs->md.resync_max_sectors) {
+ /*
+ * Sync complete.
+ */
+ array_in_sync = 1;
+ sync = rs->md.resync_max_sectors;
+ } else if (test_bit(MD_RECOVERY_REQUESTED, &rs->md.recovery)) {
+ /*
+ * If "check" or "repair" is occurring, the array has
+ * undergone and initial sync and the health characters
+ * should not be 'a' anymore.
+ */
+ array_in_sync = 1;
+ } else {
+ /*
+ * The array may be doing an initial sync, or it may
+ * be rebuilding individual components. If all the
+ * devices are In_sync, then it is the array that is
+ * being initialized.
+ */
+ for (i = 0; i < rs->md.raid_disks; i++)
+ if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
+ array_in_sync = 1;
+ }
+ } else {
+ /* RAID0 */
array_in_sync = 1;
sync = rs->md.resync_max_sectors;
- } else if (test_bit(MD_RECOVERY_REQUESTED, &rs->md.recovery)) {
- /*
- * If "check" or "repair" is occurring, the array has
- * undergone and initial sync and the health characters
- * should not be 'a' anymore.
- */
- array_in_sync = 1;
- } else {
- /*
- * The array may be doing an initial sync, or it may
- * be rebuilding individual components. If all the
- * devices are In_sync, then it is the array that is
- * being initialized.
- */
- for (i = 0; i < rs->md.raid_disks; i++)
- if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
- array_in_sync = 1;
}
/*
@@ -1446,7 +1458,7 @@ static void raid_status(struct dm_target *ti, status_type_t type,
case STATUSTYPE_TABLE:
/* The string you would use to construct this array */
for (i = 0; i < rs->md.raid_disks; i++) {
- if ((rs->print_flags & DMPF_REBUILD) &&
+ if ((rs->ctr_flags & CTR_FLAG_REBUILD) &&
rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
raid_param_cnt += 2; /* for rebuilds */
@@ -1455,33 +1467,33 @@ static void raid_status(struct dm_target *ti, status_type_t type,
raid_param_cnt += 2;
}
- raid_param_cnt += (hweight32(rs->print_flags & ~DMPF_REBUILD) * 2);
- if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
+ raid_param_cnt += (hweight32(rs->ctr_flags & ~CTR_FLAG_REBUILD) * 2);
+ if (rs->ctr_flags & (CTR_FLAG_SYNC | CTR_FLAG_NOSYNC))
raid_param_cnt--;
DMEMIT("%s %u %u", rs->raid_type->name,
raid_param_cnt, rs->md.chunk_sectors);
- if ((rs->print_flags & DMPF_SYNC) &&
+ if ((rs->ctr_flags & CTR_FLAG_SYNC) &&
(rs->md.recovery_cp == MaxSector))
DMEMIT(" sync");
- if (rs->print_flags & DMPF_NOSYNC)
+ if (rs->ctr_flags & CTR_FLAG_NOSYNC)
DMEMIT(" nosync");
for (i = 0; i < rs->md.raid_disks; i++)
- if ((rs->print_flags & DMPF_REBUILD) &&
+ if ((rs->ctr_flags & CTR_FLAG_REBUILD) &&
rs->dev[i].data_dev &&
!test_bit(In_sync, &rs->dev[i].rdev.flags))
DMEMIT(" rebuild %u", i);
- if (rs->print_flags & DMPF_DAEMON_SLEEP)
+ if (rs->ctr_flags & CTR_FLAG_DAEMON_SLEEP)
DMEMIT(" daemon_sleep %lu",
rs->md.bitmap_info.daemon_sleep);
- if (rs->print_flags & DMPF_MIN_RECOVERY_RATE)
+ if (rs->ctr_flags & CTR_FLAG_MIN_RECOVERY_RATE)
DMEMIT(" min_recovery_rate %d", rs->md.sync_speed_min);
- if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
+ if (rs->ctr_flags & CTR_FLAG_MAX_RECOVERY_RATE)
DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
for (i = 0; i < rs->md.raid_disks; i++)
@@ -1489,11 +1501,11 @@ static void raid_status(struct dm_target *ti, status_type_t type,
test_bit(WriteMostly, &rs->dev[i].rdev.flags))
DMEMIT(" write_mostly %u", i);
- if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
+ if (rs->ctr_flags & CTR_FLAG_MAX_WRITE_BEHIND)
DMEMIT(" max_write_behind %lu",
rs->md.bitmap_info.max_write_behind);
- if (rs->print_flags & DMPF_STRIPE_CACHE) {
+ if (rs->ctr_flags & CTR_FLAG_STRIPE_CACHE) {
struct r5conf *conf = rs->md.private;
/* convert from kiB to sectors */
@@ -1501,15 +1513,15 @@ static void raid_status(struct dm_target *ti, status_type_t type,
conf ? conf->max_nr_stripes * 2 : 0);
}
- if (rs->print_flags & DMPF_REGION_SIZE)
+ if (rs->ctr_flags & CTR_FLAG_REGION_SIZE)
DMEMIT(" region_size %lu",
rs->md.bitmap_info.chunksize >> 9);
- if (rs->print_flags & DMPF_RAID10_COPIES)
+ if (rs->ctr_flags & CTR_FLAG_RAID10_COPIES)
DMEMIT(" raid10_copies %u",
raid10_md_layout_to_copies(rs->md.layout));
- if (rs->print_flags & DMPF_RAID10_FORMAT)
+ if (rs->ctr_flags & CTR_FLAG_RAID10_FORMAT)
DMEMIT(" raid10_format %s",
raid10_md_layout_to_format(rs->md.layout));
@@ -1684,26 +1696,48 @@ static void raid_resume(struct dm_target *ti)
{
struct raid_set *rs = ti->private;
- set_bit(MD_CHANGE_DEVS, &rs->md.flags);
- if (!rs->bitmap_loaded) {
- bitmap_load(&rs->md);
- rs->bitmap_loaded = 1;
- } else {
- /*
- * A secondary resume while the device is active.
- * Take this opportunity to check whether any failed
- * devices are reachable again.
- */
- attempt_restore_of_faulty_devices(rs);
+ if (rs->raid_type->level) {
+ set_bit(MD_CHANGE_DEVS, &rs->md.flags);
+
+ if (!rs->bitmap_loaded) {
+ bitmap_load(&rs->md);
+ rs->bitmap_loaded = 1;
+ } else {
+ /*
+ * A secondary resume while the device is active.
+ * Take this opportunity to check whether any failed
+ * devices are reachable again.
+ */
+ attempt_restore_of_faulty_devices(rs);
+ }
+
+ clear_bit(MD_RECOVERY_FROZEN, &rs->md.recovery);
}
- clear_bit(MD_RECOVERY_FROZEN, &rs->md.recovery);
mddev_resume(&rs->md);
}
+static int raid_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct raid_set *rs = ti->private;
+ struct md_personality *pers = rs->md.pers;
+
+ if (pers && pers->mergeable_bvec)
+ return min(max_size, pers->mergeable_bvec(&rs->md, bvm, biovec));
+
+ /*
+ * In case we can't request the personality because
+ * the raid set is not running yet
+ *
+ * -> return safe minimum
+ */
+ return rs->md.chunk_sectors;
+}
+
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
@@ -1715,6 +1749,7 @@ static struct target_type raid_target = {
.presuspend = raid_presuspend,
.postsuspend = raid_postsuspend,
.resume = raid_resume,
+ .merge = raid_merge,
};
static int __init dm_raid_init(void)
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index 089d627..d83696b 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -23,8 +23,10 @@
#define MAX_RECOVERY 1 /* Maximum number of regions recovered in parallel. */
-#define DM_RAID1_HANDLE_ERRORS 0x01
+#define DM_RAID1_HANDLE_ERRORS 0x01
+#define DM_RAID1_KEEP_LOG 0x02
#define errors_handled(p) ((p)->features & DM_RAID1_HANDLE_ERRORS)
+#define keep_log(p) ((p)->features & DM_RAID1_KEEP_LOG)
static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
@@ -229,7 +231,7 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
if (m != get_default_mirror(ms))
goto out;
- if (!ms->in_sync) {
+ if (!ms->in_sync && !keep_log(ms)) {
/*
* Better to issue requests to same failing device
* than to risk returning corrupt data.
@@ -370,6 +372,17 @@ static int recover(struct mirror_set *ms, struct dm_region *reg)
return r;
}
+static void reset_ms_flags(struct mirror_set *ms)
+{
+ unsigned int m;
+
+ ms->leg_failure = 0;
+ for (m = 0; m < ms->nr_mirrors; m++) {
+ atomic_set(&(ms->mirror[m].error_count), 0);
+ ms->mirror[m].error_type = 0;
+ }
+}
+
static void do_recovery(struct mirror_set *ms)
{
struct dm_region *reg;
@@ -398,6 +411,7 @@ static void do_recovery(struct mirror_set *ms)
/* the sync is complete */
dm_table_event(ms->ti->table);
ms->in_sync = 1;
+ reset_ms_flags(ms);
}
}
@@ -759,7 +773,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
dm_rh_delay(ms->rh, bio);
while ((bio = bio_list_pop(&nosync))) {
- if (unlikely(ms->leg_failure) && errors_handled(ms)) {
+ if (unlikely(ms->leg_failure) && errors_handled(ms) && !keep_log(ms)) {
spin_lock_irq(&ms->lock);
bio_list_add(&ms->failures, bio);
spin_unlock_irq(&ms->lock);
@@ -803,15 +817,21 @@ static void do_failures(struct mirror_set *ms, struct bio_list *failures)
/*
* If all the legs are dead, fail the I/O.
- * If we have been told to handle errors, hold the bio
- * and wait for userspace to deal with the problem.
+ * If the device has failed and keep_log is enabled,
+ * fail the I/O.
+ *
+ * If we have been told to handle errors, and keep_log
+ * isn't enabled, hold the bio and wait for userspace to
+ * deal with the problem.
+ *
* Otherwise pretend that the I/O succeeded. (This would
* be wrong if the failed leg returned after reboot and
* got replicated back to the good legs.)
*/
- if (!get_valid_mirror(ms))
+
+ if (unlikely(!get_valid_mirror(ms) || (keep_log(ms) && ms->log_failure)))
bio_endio(bio, -EIO);
- else if (errors_handled(ms))
+ else if (errors_handled(ms) && !keep_log(ms))
hold_bio(ms, bio);
else
bio_endio(bio, 0);
@@ -987,6 +1007,7 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
unsigned num_features;
struct dm_target *ti = ms->ti;
char dummy;
+ int i;
*args_used = 0;
@@ -1007,15 +1028,25 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
return -EINVAL;
}
- if (!strcmp("handle_errors", argv[0]))
- ms->features |= DM_RAID1_HANDLE_ERRORS;
- else {
- ti->error = "Unrecognised feature requested";
+ for (i = 0; i < num_features; i++) {
+ if (!strcmp("handle_errors", argv[0]))
+ ms->features |= DM_RAID1_HANDLE_ERRORS;
+ else if (!strcmp("keep_log", argv[0]))
+ ms->features |= DM_RAID1_KEEP_LOG;
+ else {
+ ti->error = "Unrecognised feature requested";
+ return -EINVAL;
+ }
+
+ argc--;
+ argv++;
+ (*args_used)++;
+ }
+ if (!errors_handled(ms) && keep_log(ms)) {
+ ti->error = "keep_log feature requires the handle_errors feature";
return -EINVAL;
}
- (*args_used)++;
-
return 0;
}
@@ -1029,7 +1060,7 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
* log_type is "core" or "disk"
* #log_params is between 1 and 3
*
- * If present, features must be "handle_errors".
+ * If present, supported features are "handle_errors" and "keep_log".
*/
static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
@@ -1254,8 +1285,6 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, int error)
dm_bio_restore(bd, bio);
bio_record->details.bi_bdev = NULL;
- atomic_inc(&bio->bi_remaining);
-
queue_bio(ms, bio, rw);
return DM_ENDIO_INCOMPLETE;
}
@@ -1365,6 +1394,7 @@ static void mirror_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
unsigned int m, sz = 0;
+ int num_feature_args = 0;
struct mirror_set *ms = (struct mirror_set *) ti->private;
struct dm_dirty_log *log = dm_rh_dirty_log(ms->rh);
char buffer[ms->nr_mirrors + 1];
@@ -1394,8 +1424,17 @@ static void mirror_status(struct dm_target *ti, status_type_t type,
DMEMIT(" %s %llu", ms->mirror[m].dev->name,
(unsigned long long)ms->mirror[m].offset);
- if (ms->features & DM_RAID1_HANDLE_ERRORS)
- DMEMIT(" 1 handle_errors");
+ num_feature_args += !!errors_handled(ms);
+ num_feature_args += !!keep_log(ms);
+ if (num_feature_args) {
+ DMEMIT(" %d", num_feature_args);
+ if (errors_handled(ms))
+ DMEMIT(" handle_errors");
+ if (keep_log(ms))
+ DMEMIT(" keep_log");
+ }
+
+ break;
}
}
@@ -1415,7 +1454,7 @@ static int mirror_iterate_devices(struct dm_target *ti,
static struct target_type mirror_target = {
.name = "mirror",
- .version = {1, 13, 2},
+ .version = {1, 14, 0},
.module = THIS_MODULE,
.ctr = mirror_ctr,
.dtr = mirror_dtr,
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index f83a0f3..7c82d3c 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1478,7 +1478,6 @@ out:
if (full_bio) {
full_bio->bi_end_io = pe->full_bio_end_io;
full_bio->bi_private = pe->full_bio_private;
- atomic_inc(&full_bio->bi_remaining);
}
increment_pending_exceptions_done_count();
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index f478a4c..8a8b48f 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -29,30 +29,37 @@ struct dm_stat_percpu {
unsigned long long io_ticks[2];
unsigned long long io_ticks_total;
unsigned long long time_in_queue;
+ unsigned long long *histogram;
};
struct dm_stat_shared {
atomic_t in_flight[2];
- unsigned long stamp;
+ unsigned long long stamp;
struct dm_stat_percpu tmp;
};
struct dm_stat {
struct list_head list_entry;
int id;
+ unsigned stat_flags;
size_t n_entries;
sector_t start;
sector_t end;
sector_t step;
+ unsigned n_histogram_entries;
+ unsigned long long *histogram_boundaries;
const char *program_id;
const char *aux_data;
struct rcu_head rcu_head;
size_t shared_alloc_size;
size_t percpu_alloc_size;
+ size_t histogram_alloc_size;
struct dm_stat_percpu *stat_percpu[NR_CPUS];
struct dm_stat_shared stat_shared[0];
};
+#define STAT_PRECISE_TIMESTAMPS 1
+
struct dm_stats_last_position {
sector_t last_sector;
unsigned last_rw;
@@ -160,10 +167,7 @@ static void dm_kvfree(void *ptr, size_t alloc_size)
free_shared_memory(alloc_size);
- if (is_vmalloc_addr(ptr))
- vfree(ptr);
- else
- kfree(ptr);
+ kvfree(ptr);
}
static void dm_stat_free(struct rcu_head *head)
@@ -173,8 +177,11 @@ static void dm_stat_free(struct rcu_head *head)
kfree(s->program_id);
kfree(s->aux_data);
- for_each_possible_cpu(cpu)
+ for_each_possible_cpu(cpu) {
+ dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+ }
+ dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size);
dm_kvfree(s, s->shared_alloc_size);
}
@@ -227,7 +234,10 @@ void dm_stats_cleanup(struct dm_stats *stats)
}
static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
- sector_t step, const char *program_id, const char *aux_data,
+ sector_t step, unsigned stat_flags,
+ unsigned n_histogram_entries,
+ unsigned long long *histogram_boundaries,
+ const char *program_id, const char *aux_data,
void (*suspend_callback)(struct mapped_device *),
void (*resume_callback)(struct mapped_device *),
struct mapped_device *md)
@@ -238,6 +248,7 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
size_t ni;
size_t shared_alloc_size;
size_t percpu_alloc_size;
+ size_t histogram_alloc_size;
struct dm_stat_percpu *p;
int cpu;
int ret_id;
@@ -261,19 +272,34 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
if (percpu_alloc_size / sizeof(struct dm_stat_percpu) != n_entries)
return -EOVERFLOW;
- if (!check_shared_memory(shared_alloc_size + num_possible_cpus() * percpu_alloc_size))
+ histogram_alloc_size = (n_histogram_entries + 1) * (size_t)n_entries * sizeof(unsigned long long);
+ if (histogram_alloc_size / (n_histogram_entries + 1) != (size_t)n_entries * sizeof(unsigned long long))
+ return -EOVERFLOW;
+
+ if (!check_shared_memory(shared_alloc_size + histogram_alloc_size +
+ num_possible_cpus() * (percpu_alloc_size + histogram_alloc_size)))
return -ENOMEM;
s = dm_kvzalloc(shared_alloc_size, NUMA_NO_NODE);
if (!s)
return -ENOMEM;
+ s->stat_flags = stat_flags;
s->n_entries = n_entries;
s->start = start;
s->end = end;
s->step = step;
s->shared_alloc_size = shared_alloc_size;
s->percpu_alloc_size = percpu_alloc_size;
+ s->histogram_alloc_size = histogram_alloc_size;
+
+ s->n_histogram_entries = n_histogram_entries;
+ s->histogram_boundaries = kmemdup(histogram_boundaries,
+ s->n_histogram_entries * sizeof(unsigned long long), GFP_KERNEL);
+ if (!s->histogram_boundaries) {
+ r = -ENOMEM;
+ goto out;
+ }
s->program_id = kstrdup(program_id, GFP_KERNEL);
if (!s->program_id) {
@@ -291,6 +317,19 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
atomic_set(&s->stat_shared[ni].in_flight[WRITE], 0);
}
+ if (s->n_histogram_entries) {
+ unsigned long long *hi;
+ hi = dm_kvzalloc(s->histogram_alloc_size, NUMA_NO_NODE);
+ if (!hi) {
+ r = -ENOMEM;
+ goto out;
+ }
+ for (ni = 0; ni < n_entries; ni++) {
+ s->stat_shared[ni].tmp.histogram = hi;
+ hi += s->n_histogram_entries + 1;
+ }
+ }
+
for_each_possible_cpu(cpu) {
p = dm_kvzalloc(percpu_alloc_size, cpu_to_node(cpu));
if (!p) {
@@ -298,6 +337,18 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
goto out;
}
s->stat_percpu[cpu] = p;
+ if (s->n_histogram_entries) {
+ unsigned long long *hi;
+ hi = dm_kvzalloc(s->histogram_alloc_size, cpu_to_node(cpu));
+ if (!hi) {
+ r = -ENOMEM;
+ goto out;
+ }
+ for (ni = 0; ni < n_entries; ni++) {
+ p[ni].histogram = hi;
+ hi += s->n_histogram_entries + 1;
+ }
+ }
}
/*
@@ -375,9 +426,11 @@ static int dm_stats_delete(struct dm_stats *stats, int id)
* vfree can't be called from RCU callback
*/
for_each_possible_cpu(cpu)
- if (is_vmalloc_addr(s->stat_percpu))
+ if (is_vmalloc_addr(s->stat_percpu) ||
+ is_vmalloc_addr(s->stat_percpu[cpu][0].histogram))
goto do_sync_free;
- if (is_vmalloc_addr(s)) {
+ if (is_vmalloc_addr(s) ||
+ is_vmalloc_addr(s->stat_shared[0].tmp.histogram)) {
do_sync_free:
synchronize_rcu_expedited();
dm_stat_free(&s->rcu_head);
@@ -417,18 +470,24 @@ static int dm_stats_list(struct dm_stats *stats, const char *program,
return 1;
}
-static void dm_stat_round(struct dm_stat_shared *shared, struct dm_stat_percpu *p)
+static void dm_stat_round(struct dm_stat *s, struct dm_stat_shared *shared,
+ struct dm_stat_percpu *p)
{
/*
* This is racy, but so is part_round_stats_single.
*/
- unsigned long now = jiffies;
- unsigned in_flight_read;
- unsigned in_flight_write;
- unsigned long difference = now - shared->stamp;
+ unsigned long long now, difference;
+ unsigned in_flight_read, in_flight_write;
+
+ if (likely(!(s->stat_flags & STAT_PRECISE_TIMESTAMPS)))
+ now = jiffies;
+ else
+ now = ktime_to_ns(ktime_get());
+ difference = now - shared->stamp;
if (!difference)
return;
+
in_flight_read = (unsigned)atomic_read(&shared->in_flight[READ]);
in_flight_write = (unsigned)atomic_read(&shared->in_flight[WRITE]);
if (in_flight_read)
@@ -443,8 +502,9 @@ static void dm_stat_round(struct dm_stat_shared *shared, struct dm_stat_percpu *
}
static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
- unsigned long bi_rw, sector_t len, bool merged,
- bool end, unsigned long duration)
+ unsigned long bi_rw, sector_t len,
+ struct dm_stats_aux *stats_aux, bool end,
+ unsigned long duration_jiffies)
{
unsigned long idx = bi_rw & REQ_WRITE;
struct dm_stat_shared *shared = &s->stat_shared[entry];
@@ -474,15 +534,35 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
p = &s->stat_percpu[smp_processor_id()][entry];
if (!end) {
- dm_stat_round(shared, p);
+ dm_stat_round(s, shared, p);
atomic_inc(&shared->in_flight[idx]);
} else {
- dm_stat_round(shared, p);
+ unsigned long long duration;
+ dm_stat_round(s, shared, p);
atomic_dec(&shared->in_flight[idx]);
p->sectors[idx] += len;
p->ios[idx] += 1;
- p->merges[idx] += merged;
- p->ticks[idx] += duration;
+ p->merges[idx] += stats_aux->merged;
+ if (!(s->stat_flags & STAT_PRECISE_TIMESTAMPS)) {
+ p->ticks[idx] += duration_jiffies;
+ duration = jiffies_to_msecs(duration_jiffies);
+ } else {
+ p->ticks[idx] += stats_aux->duration_ns;
+ duration = stats_aux->duration_ns;
+ }
+ if (s->n_histogram_entries) {
+ unsigned lo = 0, hi = s->n_histogram_entries + 1;
+ while (lo + 1 < hi) {
+ unsigned mid = (lo + hi) / 2;
+ if (s->histogram_boundaries[mid - 1] > duration) {
+ hi = mid;
+ } else {
+ lo = mid;
+ }
+
+ }
+ p->histogram[lo]++;
+ }
}
#if BITS_PER_LONG == 32
@@ -494,7 +574,7 @@ static void dm_stat_for_entry(struct dm_stat *s, size_t entry,
static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
sector_t bi_sector, sector_t end_sector,
- bool end, unsigned long duration,
+ bool end, unsigned long duration_jiffies,
struct dm_stats_aux *stats_aux)
{
sector_t rel_sector, offset, todo, fragment_len;
@@ -523,7 +603,7 @@ static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
if (fragment_len > s->step - offset)
fragment_len = s->step - offset;
dm_stat_for_entry(s, entry, bi_rw, fragment_len,
- stats_aux->merged, end, duration);
+ stats_aux, end, duration_jiffies);
todo -= fragment_len;
entry++;
offset = 0;
@@ -532,11 +612,13 @@ static void __dm_stat_bio(struct dm_stat *s, unsigned long bi_rw,
void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
sector_t bi_sector, unsigned bi_sectors, bool end,
- unsigned long duration, struct dm_stats_aux *stats_aux)
+ unsigned long duration_jiffies,
+ struct dm_stats_aux *stats_aux)
{
struct dm_stat *s;
sector_t end_sector;
struct dm_stats_last_position *last;
+ bool got_precise_time;
if (unlikely(!bi_sectors))
return;
@@ -560,8 +642,17 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
rcu_read_lock();
- list_for_each_entry_rcu(s, &stats->list, list_entry)
- __dm_stat_bio(s, bi_rw, bi_sector, end_sector, end, duration, stats_aux);
+ got_precise_time = false;
+ list_for_each_entry_rcu(s, &stats->list, list_entry) {
+ if (s->stat_flags & STAT_PRECISE_TIMESTAMPS && !got_precise_time) {
+ if (!end)
+ stats_aux->duration_ns = ktime_to_ns(ktime_get());
+ else
+ stats_aux->duration_ns = ktime_to_ns(ktime_get()) - stats_aux->duration_ns;
+ got_precise_time = true;
+ }
+ __dm_stat_bio(s, bi_rw, bi_sector, end_sector, end, duration_jiffies, stats_aux);
+ }
rcu_read_unlock();
}
@@ -574,10 +665,25 @@ static void __dm_stat_init_temporary_percpu_totals(struct dm_stat_shared *shared
local_irq_disable();
p = &s->stat_percpu[smp_processor_id()][x];
- dm_stat_round(shared, p);
+ dm_stat_round(s, shared, p);
local_irq_enable();
- memset(&shared->tmp, 0, sizeof(shared->tmp));
+ shared->tmp.sectors[READ] = 0;
+ shared->tmp.sectors[WRITE] = 0;
+ shared->tmp.ios[READ] = 0;
+ shared->tmp.ios[WRITE] = 0;
+ shared->tmp.merges[READ] = 0;
+ shared->tmp.merges[WRITE] = 0;
+ shared->tmp.ticks[READ] = 0;
+ shared->tmp.ticks[WRITE] = 0;
+ shared->tmp.io_ticks[READ] = 0;
+ shared->tmp.io_ticks[WRITE] = 0;
+ shared->tmp.io_ticks_total = 0;
+ shared->tmp.time_in_queue = 0;
+
+ if (s->n_histogram_entries)
+ memset(shared->tmp.histogram, 0, (s->n_histogram_entries + 1) * sizeof(unsigned long long));
+
for_each_possible_cpu(cpu) {
p = &s->stat_percpu[cpu][x];
shared->tmp.sectors[READ] += ACCESS_ONCE(p->sectors[READ]);
@@ -592,6 +698,11 @@ static void __dm_stat_init_temporary_percpu_totals(struct dm_stat_shared *shared
shared->tmp.io_ticks[WRITE] += ACCESS_ONCE(p->io_ticks[WRITE]);
shared->tmp.io_ticks_total += ACCESS_ONCE(p->io_ticks_total);
shared->tmp.time_in_queue += ACCESS_ONCE(p->time_in_queue);
+ if (s->n_histogram_entries) {
+ unsigned i;
+ for (i = 0; i < s->n_histogram_entries + 1; i++)
+ shared->tmp.histogram[i] += ACCESS_ONCE(p->histogram[i]);
+ }
}
}
@@ -621,6 +732,15 @@ static void __dm_stat_clear(struct dm_stat *s, size_t idx_start, size_t idx_end,
p->io_ticks_total -= shared->tmp.io_ticks_total;
p->time_in_queue -= shared->tmp.time_in_queue;
local_irq_enable();
+ if (s->n_histogram_entries) {
+ unsigned i;
+ for (i = 0; i < s->n_histogram_entries + 1; i++) {
+ local_irq_disable();
+ p = &s->stat_percpu[smp_processor_id()][x];
+ p->histogram[i] -= shared->tmp.histogram[i];
+ local_irq_enable();
+ }
+ }
}
}
@@ -646,11 +766,15 @@ static int dm_stats_clear(struct dm_stats *stats, int id)
/*
* This is like jiffies_to_msec, but works for 64-bit values.
*/
-static unsigned long long dm_jiffies_to_msec64(unsigned long long j)
+static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long long j)
{
- unsigned long long result = 0;
+ unsigned long long result;
unsigned mult;
+ if (s->stat_flags & STAT_PRECISE_TIMESTAMPS)
+ return j;
+
+ result = 0;
if (j)
result = jiffies_to_msecs(j & 0x3fffff);
if (j >= 1 << 22) {
@@ -706,22 +830,29 @@ static int dm_stats_print(struct dm_stats *stats, int id,
__dm_stat_init_temporary_percpu_totals(shared, s, x);
- DMEMIT("%llu+%llu %llu %llu %llu %llu %llu %llu %llu %llu %d %llu %llu %llu %llu\n",
+ DMEMIT("%llu+%llu %llu %llu %llu %llu %llu %llu %llu %llu %d %llu %llu %llu %llu",
(unsigned long long)start,
(unsigned long long)step,
shared->tmp.ios[READ],
shared->tmp.merges[READ],
shared->tmp.sectors[READ],
- dm_jiffies_to_msec64(shared->tmp.ticks[READ]),
+ dm_jiffies_to_msec64(s, shared->tmp.ticks[READ]),
shared->tmp.ios[WRITE],
shared->tmp.merges[WRITE],
shared->tmp.sectors[WRITE],
- dm_jiffies_to_msec64(shared->tmp.ticks[WRITE]),
+ dm_jiffies_to_msec64(s, shared->tmp.ticks[WRITE]),
dm_stat_in_flight(shared),
- dm_jiffies_to_msec64(shared->tmp.io_ticks_total),
- dm_jiffies_to_msec64(shared->tmp.time_in_queue),
- dm_jiffies_to_msec64(shared->tmp.io_ticks[READ]),
- dm_jiffies_to_msec64(shared->tmp.io_ticks[WRITE]));
+ dm_jiffies_to_msec64(s, shared->tmp.io_ticks_total),
+ dm_jiffies_to_msec64(s, shared->tmp.time_in_queue),
+ dm_jiffies_to_msec64(s, shared->tmp.io_ticks[READ]),
+ dm_jiffies_to_msec64(s, shared->tmp.io_ticks[WRITE]));
+ if (s->n_histogram_entries) {
+ unsigned i;
+ for (i = 0; i < s->n_histogram_entries + 1; i++) {
+ DMEMIT("%s%llu", !i ? " " : ":", shared->tmp.histogram[i]);
+ }
+ }
+ DMEMIT("\n");
if (unlikely(sz + 1 >= maxlen))
goto buffer_overflow;
@@ -763,55 +894,134 @@ static int dm_stats_set_aux(struct dm_stats *stats, int id, const char *aux_data
return 0;
}
+static int parse_histogram(const char *h, unsigned *n_histogram_entries,
+ unsigned long long **histogram_boundaries)
+{
+ const char *q;
+ unsigned n;
+ unsigned long long last;
+
+ *n_histogram_entries = 1;
+ for (q = h; *q; q++)
+ if (*q == ',')
+ (*n_histogram_entries)++;
+
+ *histogram_boundaries = kmalloc(*n_histogram_entries * sizeof(unsigned long long), GFP_KERNEL);
+ if (!*histogram_boundaries)
+ return -ENOMEM;
+
+ n = 0;
+ last = 0;
+ while (1) {
+ unsigned long long hi;
+ int s;
+ char ch;
+ s = sscanf(h, "%llu%c", &hi, &ch);
+ if (!s || (s == 2 && ch != ','))
+ return -EINVAL;
+ if (hi <= last)
+ return -EINVAL;
+ last = hi;
+ (*histogram_boundaries)[n] = hi;
+ if (s == 1)
+ return 0;
+ h = strchr(h, ',') + 1;
+ n++;
+ }
+}
+
static int message_stats_create(struct mapped_device *md,
unsigned argc, char **argv,
char *result, unsigned maxlen)
{
+ int r;
int id;
char dummy;
unsigned long long start, end, len, step;
unsigned divisor;
const char *program_id, *aux_data;
+ unsigned stat_flags = 0;
+
+ unsigned n_histogram_entries = 0;
+ unsigned long long *histogram_boundaries = NULL;
+
+ struct dm_arg_set as, as_backup;
+ const char *a;
+ unsigned feature_args;
/*
* Input format:
- * <range> <step> [<program_id> [<aux_data>]]
+ * <range> <step> [<extra_parameters> <parameters>] [<program_id> [<aux_data>]]
*/
- if (argc < 3 || argc > 5)
- return -EINVAL;
+ if (argc < 3)
+ goto ret_einval;
- if (!strcmp(argv[1], "-")) {
+ as.argc = argc;
+ as.argv = argv;
+ dm_consume_args(&as, 1);
+
+ a = dm_shift_arg(&as);
+ if (!strcmp(a, "-")) {
start = 0;
len = dm_get_size(md);
if (!len)
len = 1;
- } else if (sscanf(argv[1], "%llu+%llu%c", &start, &len, &dummy) != 2 ||
+ } else if (sscanf(a, "%llu+%llu%c", &start, &len, &dummy) != 2 ||
start != (sector_t)start || len != (sector_t)len)
- return -EINVAL;
+ goto ret_einval;
end = start + len;
if (start >= end)
- return -EINVAL;
+ goto ret_einval;
- if (sscanf(argv[2], "/%u%c", &divisor, &dummy) == 1) {
+ a = dm_shift_arg(&as);
+ if (sscanf(a, "/%u%c", &divisor, &dummy) == 1) {
+ if (!divisor)
+ return -EINVAL;
step = end - start;
if (do_div(step, divisor))
step++;
if (!step)
step = 1;
- } else if (sscanf(argv[2], "%llu%c", &step, &dummy) != 1 ||
+ } else if (sscanf(a, "%llu%c", &step, &dummy) != 1 ||
step != (sector_t)step || !step)
- return -EINVAL;
+ goto ret_einval;
+
+ as_backup = as;
+ a = dm_shift_arg(&as);
+ if (a && sscanf(a, "%u%c", &feature_args, &dummy) == 1) {
+ while (feature_args--) {
+ a = dm_shift_arg(&as);
+ if (!a)
+ goto ret_einval;
+ if (!strcasecmp(a, "precise_timestamps"))
+ stat_flags |= STAT_PRECISE_TIMESTAMPS;
+ else if (!strncasecmp(a, "histogram:", 10)) {
+ if (n_histogram_entries)
+ goto ret_einval;
+ if ((r = parse_histogram(a + 10, &n_histogram_entries, &histogram_boundaries)))
+ goto ret;
+ } else
+ goto ret_einval;
+ }
+ } else {
+ as = as_backup;
+ }
program_id = "-";
aux_data = "-";
- if (argc > 3)
- program_id = argv[3];
+ a = dm_shift_arg(&as);
+ if (a)
+ program_id = a;
- if (argc > 4)
- aux_data = argv[4];
+ a = dm_shift_arg(&as);
+ if (a)
+ aux_data = a;
+
+ if (as.argc)
+ goto ret_einval;
/*
* If a buffer overflow happens after we created the region,
@@ -820,17 +1030,29 @@ static int message_stats_create(struct mapped_device *md,
* leaked). So we must detect buffer overflow in advance.
*/
snprintf(result, maxlen, "%d", INT_MAX);
- if (dm_message_test_buffer_overflow(result, maxlen))
- return 1;
+ if (dm_message_test_buffer_overflow(result, maxlen)) {
+ r = 1;
+ goto ret;
+ }
- id = dm_stats_create(dm_get_stats(md), start, end, step, program_id, aux_data,
+ id = dm_stats_create(dm_get_stats(md), start, end, step, stat_flags,
+ n_histogram_entries, histogram_boundaries, program_id, aux_data,
dm_internal_suspend_fast, dm_internal_resume_fast, md);
- if (id < 0)
- return id;
+ if (id < 0) {
+ r = id;
+ goto ret;
+ }
snprintf(result, maxlen, "%d", id);
- return 1;
+ r = 1;
+ goto ret;
+
+ret_einval:
+ r = -EINVAL;
+ret:
+ kfree(histogram_boundaries);
+ return r;
}
static int message_stats_delete(struct mapped_device *md,
@@ -933,11 +1155,6 @@ int dm_stats_message(struct mapped_device *md, unsigned argc, char **argv,
{
int r;
- if (dm_request_based(md)) {
- DMWARN("Statistics are only supported for bio-based devices");
- return -EOPNOTSUPP;
- }
-
/* All messages here must start with '@' */
if (!strcasecmp(argv[0], "@stats_create"))
r = message_stats_create(md, argc, argv, result, maxlen);
diff --git a/drivers/md/dm-stats.h b/drivers/md/dm-stats.h
index e7c4984..f1c0956 100644
--- a/drivers/md/dm-stats.h
+++ b/drivers/md/dm-stats.h
@@ -18,6 +18,7 @@ struct dm_stats {
struct dm_stats_aux {
bool merged;
+ unsigned long long duration_ns;
};
void dm_stats_init(struct dm_stats *st);
@@ -30,7 +31,8 @@ int dm_stats_message(struct mapped_device *md, unsigned argc, char **argv,
void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
sector_t bi_sector, unsigned bi_sectors, bool end,
- unsigned long duration, struct dm_stats_aux *aux);
+ unsigned long duration_jiffies,
+ struct dm_stats_aux *aux);
static inline bool dm_stats_used(struct dm_stats *st)
{
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index f8b37d4..a672a15 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -451,10 +451,8 @@ int __init dm_stripe_init(void)
int r;
r = dm_register_target(&stripe_target);
- if (r < 0) {
+ if (r < 0)
DMWARN("target registration failed");
- return r;
- }
return r;
}
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 16ba55a..85e1d39 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -942,23 +942,30 @@ static int dm_table_alloc_md_mempools(struct dm_table *t, struct mapped_device *
{
unsigned type = dm_table_get_type(t);
unsigned per_bio_data_size = 0;
- struct dm_target *tgt;
unsigned i;
- if (unlikely(type == DM_TYPE_NONE)) {
+ switch (type) {
+ case DM_TYPE_BIO_BASED:
+ for (i = 0; i < t->num_targets; i++) {
+ struct dm_target *tgt = t->targets + i;
+
+ per_bio_data_size = max(per_bio_data_size,
+ tgt->per_bio_data_size);
+ }
+ t->mempools = dm_alloc_bio_mempools(t->integrity_supported,
+ per_bio_data_size);
+ break;
+ case DM_TYPE_REQUEST_BASED:
+ case DM_TYPE_MQ_REQUEST_BASED:
+ t->mempools = dm_alloc_rq_mempools(md, type);
+ break;
+ default:
DMWARN("no table type is set, can't allocate mempools");
return -EINVAL;
}
- if (type == DM_TYPE_BIO_BASED)
- for (i = 0; i < t->num_targets; i++) {
- tgt = t->targets + i;
- per_bio_data_size = max(per_bio_data_size, tgt->per_bio_data_size);
- }
-
- t->mempools = dm_alloc_md_mempools(md, type, t->integrity_supported, per_bio_data_size);
- if (!t->mempools)
- return -ENOMEM;
+ if (IS_ERR(t->mempools))
+ return PTR_ERR(t->mempools);
return 0;
}
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 79f6941..48dfe3c 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -184,7 +184,6 @@ struct dm_pool_metadata {
uint64_t trans_id;
unsigned long flags;
sector_t data_block_size;
- bool read_only:1;
/*
* Set if a transaction has to be aborted but the attempt to roll back
@@ -836,7 +835,6 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
init_rwsem(&pmd->root_lock);
pmd->time = 0;
INIT_LIST_HEAD(&pmd->thin_devices);
- pmd->read_only = false;
pmd->fail_io = false;
pmd->bdev = bdev;
pmd->data_block_size = data_block_size;
@@ -880,7 +878,7 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
return -EBUSY;
}
- if (!pmd->read_only && !pmd->fail_io) {
+ if (!dm_bm_is_read_only(pmd->bm) && !pmd->fail_io) {
r = __commit_transaction(pmd);
if (r < 0)
DMWARN("%s: __commit_transaction() failed, error = %d",
@@ -1392,10 +1390,11 @@ int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
dm_block_t keys[2] = { td->id, block };
struct dm_btree_info *info;
- if (pmd->fail_io)
- return -EINVAL;
-
down_read(&pmd->root_lock);
+ if (pmd->fail_io) {
+ up_read(&pmd->root_lock);
+ return -EINVAL;
+ }
if (can_issue_io) {
info = &pmd->info;
@@ -1419,6 +1418,63 @@ int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
return r;
}
+/* FIXME: write a more efficient one in btree */
+int dm_thin_find_mapped_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end,
+ dm_block_t *thin_begin, dm_block_t *thin_end,
+ dm_block_t *pool_begin, bool *maybe_shared)
+{
+ int r;
+ dm_block_t pool_end;
+ struct dm_thin_lookup_result lookup;
+
+ if (end < begin)
+ return -ENODATA;
+
+ /*
+ * Find first mapped block.
+ */
+ while (begin < end) {
+ r = dm_thin_find_block(td, begin, true, &lookup);
+ if (r) {
+ if (r != -ENODATA)
+ return r;
+ } else
+ break;
+
+ begin++;
+ }
+
+ if (begin == end)
+ return -ENODATA;
+
+ *thin_begin = begin;
+ *pool_begin = lookup.block;
+ *maybe_shared = lookup.shared;
+
+ begin++;
+ pool_end = *pool_begin + 1;
+ while (begin != end) {
+ r = dm_thin_find_block(td, begin, true, &lookup);
+ if (r) {
+ if (r == -ENODATA)
+ break;
+ else
+ return r;
+ }
+
+ if ((lookup.block != pool_end) ||
+ (lookup.shared != *maybe_shared))
+ break;
+
+ pool_end++;
+ begin++;
+ }
+
+ *thin_end = begin;
+ return 0;
+}
+
static int __insert(struct dm_thin_device *td, dm_block_t block,
dm_block_t data_block)
{
@@ -1471,6 +1527,47 @@ static int __remove(struct dm_thin_device *td, dm_block_t block)
return 0;
}
+static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end)
+{
+ int r;
+ unsigned count;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[1] = { td->id };
+ __le64 value;
+ dm_block_t mapping_root;
+
+ /*
+ * Find the mapping tree
+ */
+ r = dm_btree_lookup(&pmd->tl_info, pmd->root, keys, &value);
+ if (r)
+ return r;
+
+ /*
+ * Remove from the mapping tree, taking care to inc the
+ * ref count so it doesn't get deleted.
+ */
+ mapping_root = le64_to_cpu(value);
+ dm_tm_inc(pmd->tm, mapping_root);
+ r = dm_btree_remove(&pmd->tl_info, pmd->root, keys, &pmd->root);
+ if (r)
+ return r;
+
+ r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count);
+ if (r)
+ return r;
+
+ td->mapped_blocks -= count;
+ td->changed = 1;
+
+ /*
+ * Reinsert the mapping tree.
+ */
+ value = cpu_to_le64(mapping_root);
+ __dm_bless_for_disk(&value);
+ return dm_btree_insert(&pmd->tl_info, pmd->root, keys, &value, &pmd->root);
+}
+
int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
{
int r = -EINVAL;
@@ -1483,6 +1580,19 @@ int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
return r;
}
+int dm_thin_remove_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end)
+{
+ int r = -EINVAL;
+
+ down_write(&td->pmd->root_lock);
+ if (!td->pmd->fail_io)
+ r = __remove_range(td, begin, end);
+ up_write(&td->pmd->root_lock);
+
+ return r;
+}
+
int dm_pool_block_is_used(struct dm_pool_metadata *pmd, dm_block_t b, bool *result)
{
int r;
@@ -1739,7 +1849,6 @@ int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_cou
void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
{
down_write(&pmd->root_lock);
- pmd->read_only = true;
dm_bm_set_read_only(pmd->bm);
up_write(&pmd->root_lock);
}
@@ -1747,7 +1856,6 @@ void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
void dm_pool_metadata_read_write(struct dm_pool_metadata *pmd)
{
down_write(&pmd->root_lock);
- pmd->read_only = false;
dm_bm_set_read_write(pmd->bm);
up_write(&pmd->root_lock);
}
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index fac01a9..a938bab 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -147,6 +147,15 @@ int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
int can_issue_io, struct dm_thin_lookup_result *result);
/*
+ * Retrieve the next run of contiguously mapped blocks. Useful for working
+ * out where to break up IO. Returns 0 on success, < 0 on error.
+ */
+int dm_thin_find_mapped_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end,
+ dm_block_t *thin_begin, dm_block_t *thin_end,
+ dm_block_t *pool_begin, bool *maybe_shared);
+
+/*
* Obtain an unused block.
*/
int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result);
@@ -158,6 +167,8 @@ int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
dm_block_t data_block);
int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block);
+int dm_thin_remove_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end);
/*
* Queries.
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 921aafd..c33f61a4 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -111,22 +111,30 @@ DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(snapshot_copy_throttle,
/*
* Key building.
*/
-static void build_data_key(struct dm_thin_device *td,
- dm_block_t b, struct dm_cell_key *key)
+enum lock_space {
+ VIRTUAL,
+ PHYSICAL
+};
+
+static void build_key(struct dm_thin_device *td, enum lock_space ls,
+ dm_block_t b, dm_block_t e, struct dm_cell_key *key)
{
- key->virtual = 0;
+ key->virtual = (ls == VIRTUAL);
key->dev = dm_thin_dev_id(td);
key->block_begin = b;
- key->block_end = b + 1ULL;
+ key->block_end = e;
+}
+
+static void build_data_key(struct dm_thin_device *td, dm_block_t b,
+ struct dm_cell_key *key)
+{
+ build_key(td, PHYSICAL, b, b + 1llu, key);
}
static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
struct dm_cell_key *key)
{
- key->virtual = 1;
- key->dev = dm_thin_dev_id(td);
- key->block_begin = b;
- key->block_end = b + 1ULL;
+ build_key(td, VIRTUAL, b, b + 1llu, key);
}
/*----------------------------------------------------------------*/
@@ -312,6 +320,138 @@ struct thin_c {
/*----------------------------------------------------------------*/
+/**
+ * __blkdev_issue_discard_async - queue a discard with async completion
+ * @bdev: blockdev to issue discard for
+ * @sector: start sector
+ * @nr_sects: number of sectors to discard
+ * @gfp_mask: memory allocation flags (for bio_alloc)
+ * @flags: BLKDEV_IFL_* flags to control behaviour
+ * @parent_bio: parent discard bio that all sub discards get chained to
+ *
+ * Description:
+ * Asynchronously issue a discard request for the sectors in question.
+ * NOTE: this variant of blk-core's blkdev_issue_discard() is a stop-gap
+ * that is being kept local to DM thinp until the block changes to allow
+ * late bio splitting land upstream.
+ */
+static int __blkdev_issue_discard_async(struct block_device *bdev, sector_t sector,
+ sector_t nr_sects, gfp_t gfp_mask, unsigned long flags,
+ struct bio *parent_bio)
+{
+ struct request_queue *q = bdev_get_queue(bdev);
+ int type = REQ_WRITE | REQ_DISCARD;
+ unsigned int max_discard_sectors, granularity;
+ int alignment;
+ struct bio *bio;
+ int ret = 0;
+ struct blk_plug plug;
+
+ if (!q)
+ return -ENXIO;
+
+ if (!blk_queue_discard(q))
+ return -EOPNOTSUPP;
+
+ /* Zero-sector (unknown) and one-sector granularities are the same. */
+ granularity = max(q->limits.discard_granularity >> 9, 1U);
+ alignment = (bdev_discard_alignment(bdev) >> 9) % granularity;
+
+ /*
+ * Ensure that max_discard_sectors is of the proper
+ * granularity, so that requests stay aligned after a split.
+ */
+ max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9);
+ max_discard_sectors -= max_discard_sectors % granularity;
+ if (unlikely(!max_discard_sectors)) {
+ /* Avoid infinite loop below. Being cautious never hurts. */
+ return -EOPNOTSUPP;
+ }
+
+ if (flags & BLKDEV_DISCARD_SECURE) {
+ if (!blk_queue_secdiscard(q))
+ return -EOPNOTSUPP;
+ type |= REQ_SECURE;
+ }
+
+ blk_start_plug(&plug);
+ while (nr_sects) {
+ unsigned int req_sects;
+ sector_t end_sect, tmp;
+
+ /*
+ * Required bio_put occurs in bio_endio thanks to bio_chain below
+ */
+ bio = bio_alloc(gfp_mask, 1);
+ if (!bio) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ req_sects = min_t(sector_t, nr_sects, max_discard_sectors);
+
+ /*
+ * If splitting a request, and the next starting sector would be
+ * misaligned, stop the discard at the previous aligned sector.
+ */
+ end_sect = sector + req_sects;
+ tmp = end_sect;
+ if (req_sects < nr_sects &&
+ sector_div(tmp, granularity) != alignment) {
+ end_sect = end_sect - alignment;
+ sector_div(end_sect, granularity);
+ end_sect = end_sect * granularity + alignment;
+ req_sects = end_sect - sector;
+ }
+
+ bio_chain(bio, parent_bio);
+
+ bio->bi_iter.bi_sector = sector;
+ bio->bi_bdev = bdev;
+
+ bio->bi_iter.bi_size = req_sects << 9;
+ nr_sects -= req_sects;
+ sector = end_sect;
+
+ submit_bio(type, bio);
+
+ /*
+ * We can loop for a long time in here, if someone does
+ * full device discards (like mkfs). Be nice and allow
+ * us to schedule out to avoid softlocking if preempt
+ * is disabled.
+ */
+ cond_resched();
+ }
+ blk_finish_plug(&plug);
+
+ return ret;
+}
+
+static bool block_size_is_power_of_two(struct pool *pool)
+{
+ return pool->sectors_per_block_shift >= 0;
+}
+
+static sector_t block_to_sectors(struct pool *pool, dm_block_t b)
+{
+ return block_size_is_power_of_two(pool) ?
+ (b << pool->sectors_per_block_shift) :
+ (b * pool->sectors_per_block);
+}
+
+static int issue_discard(struct thin_c *tc, dm_block_t data_b, dm_block_t data_e,
+ struct bio *parent_bio)
+{
+ sector_t s = block_to_sectors(tc->pool, data_b);
+ sector_t len = block_to_sectors(tc->pool, data_e - data_b);
+
+ return __blkdev_issue_discard_async(tc->pool_dev->bdev, s, len,
+ GFP_NOWAIT, 0, parent_bio);
+}
+
+/*----------------------------------------------------------------*/
+
/*
* wake_worker() is used when new work is queued and when pool_resume is
* ready to continue deferred IO processing.
@@ -461,6 +601,7 @@ struct dm_thin_endio_hook {
struct dm_deferred_entry *all_io_entry;
struct dm_thin_new_mapping *overwrite_mapping;
struct rb_node rb_node;
+ struct dm_bio_prison_cell *cell;
};
static void __merge_bio_list(struct bio_list *bios, struct bio_list *master)
@@ -541,11 +682,6 @@ static void error_retry_list(struct pool *pool)
* target.
*/
-static bool block_size_is_power_of_two(struct pool *pool)
-{
- return pool->sectors_per_block_shift >= 0;
-}
-
static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
{
struct pool *pool = tc->pool;
@@ -559,6 +695,34 @@ static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
return block_nr;
}
+/*
+ * Returns the _complete_ blocks that this bio covers.
+ */
+static void get_bio_block_range(struct thin_c *tc, struct bio *bio,
+ dm_block_t *begin, dm_block_t *end)
+{
+ struct pool *pool = tc->pool;
+ sector_t b = bio->bi_iter.bi_sector;
+ sector_t e = b + (bio->bi_iter.bi_size >> SECTOR_SHIFT);
+
+ b += pool->sectors_per_block - 1ull; /* so we round up */
+
+ if (block_size_is_power_of_two(pool)) {
+ b >>= pool->sectors_per_block_shift;
+ e >>= pool->sectors_per_block_shift;
+ } else {
+ (void) sector_div(b, pool->sectors_per_block);
+ (void) sector_div(e, pool->sectors_per_block);
+ }
+
+ if (e < b)
+ /* Can happen if the bio is within a single block. */
+ e = b;
+
+ *begin = b;
+ *end = e;
+}
+
static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
{
struct pool *pool = tc->pool;
@@ -647,7 +811,7 @@ struct dm_thin_new_mapping {
struct list_head list;
bool pass_discard:1;
- bool definitely_not_shared:1;
+ bool maybe_shared:1;
/*
* Track quiescing, copying and zeroing preparation actions. When this
@@ -658,9 +822,9 @@ struct dm_thin_new_mapping {
int err;
struct thin_c *tc;
- dm_block_t virt_block;
+ dm_block_t virt_begin, virt_end;
dm_block_t data_block;
- struct dm_bio_prison_cell *cell, *cell2;
+ struct dm_bio_prison_cell *cell;
/*
* If the bio covers the whole area of a block then we can avoid
@@ -705,6 +869,8 @@ static void overwrite_endio(struct bio *bio, int err)
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
struct dm_thin_new_mapping *m = h->overwrite_mapping;
+ bio->bi_end_io = m->saved_bi_end_io;
+
m->err = err;
complete_mapping_preparation(m);
}
@@ -793,10 +959,6 @@ static void inc_remap_and_issue_cell(struct thin_c *tc,
static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m)
{
- if (m->bio) {
- m->bio->bi_end_io = m->saved_bi_end_io;
- atomic_inc(&m->bio->bi_remaining);
- }
cell_error(m->tc->pool, m->cell);
list_del(&m->list);
mempool_free(m, m->tc->pool->mapping_pool);
@@ -806,15 +968,9 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
{
struct thin_c *tc = m->tc;
struct pool *pool = tc->pool;
- struct bio *bio;
+ struct bio *bio = m->bio;
int r;
- bio = m->bio;
- if (bio) {
- bio->bi_end_io = m->saved_bi_end_io;
- atomic_inc(&bio->bi_remaining);
- }
-
if (m->err) {
cell_error(pool, m->cell);
goto out;
@@ -825,7 +981,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
* Any I/O for this block arriving after this point will get
* remapped to it directly.
*/
- r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block);
+ r = dm_thin_insert_block(tc->td, m->virt_begin, m->data_block);
if (r) {
metadata_operation_failed(pool, "dm_thin_insert_block", r);
cell_error(pool, m->cell);
@@ -852,50 +1008,112 @@ out:
mempool_free(m, pool->mapping_pool);
}
-static void process_prepared_discard_fail(struct dm_thin_new_mapping *m)
+/*----------------------------------------------------------------*/
+
+static void free_discard_mapping(struct dm_thin_new_mapping *m)
{
struct thin_c *tc = m->tc;
+ if (m->cell)
+ cell_defer_no_holder(tc, m->cell);
+ mempool_free(m, tc->pool->mapping_pool);
+}
+static void process_prepared_discard_fail(struct dm_thin_new_mapping *m)
+{
bio_io_error(m->bio);
+ free_discard_mapping(m);
+}
+
+static void process_prepared_discard_success(struct dm_thin_new_mapping *m)
+{
+ bio_endio(m->bio, 0);
+ free_discard_mapping(m);
+}
+
+static void process_prepared_discard_no_passdown(struct dm_thin_new_mapping *m)
+{
+ int r;
+ struct thin_c *tc = m->tc;
+
+ r = dm_thin_remove_range(tc->td, m->cell->key.block_begin, m->cell->key.block_end);
+ if (r) {
+ metadata_operation_failed(tc->pool, "dm_thin_remove_range", r);
+ bio_io_error(m->bio);
+ } else
+ bio_endio(m->bio, 0);
+
cell_defer_no_holder(tc, m->cell);
- cell_defer_no_holder(tc, m->cell2);
mempool_free(m, tc->pool->mapping_pool);
}
-static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
+static int passdown_double_checking_shared_status(struct dm_thin_new_mapping *m)
{
+ /*
+ * We've already unmapped this range of blocks, but before we
+ * passdown we have to check that these blocks are now unused.
+ */
+ int r;
+ bool used = true;
struct thin_c *tc = m->tc;
+ struct pool *pool = tc->pool;
+ dm_block_t b = m->data_block, e, end = m->data_block + m->virt_end - m->virt_begin;
- inc_all_io_entry(tc->pool, m->bio);
- cell_defer_no_holder(tc, m->cell);
- cell_defer_no_holder(tc, m->cell2);
+ while (b != end) {
+ /* find start of unmapped run */
+ for (; b < end; b++) {
+ r = dm_pool_block_is_used(pool->pmd, b, &used);
+ if (r)
+ return r;
- if (m->pass_discard)
- if (m->definitely_not_shared)
- remap_and_issue(tc, m->bio, m->data_block);
- else {
- bool used = false;
- if (dm_pool_block_is_used(tc->pool->pmd, m->data_block, &used) || used)
- bio_endio(m->bio, 0);
- else
- remap_and_issue(tc, m->bio, m->data_block);
+ if (!used)
+ break;
}
- else
- bio_endio(m->bio, 0);
- mempool_free(m, tc->pool->mapping_pool);
+ if (b == end)
+ break;
+
+ /* find end of run */
+ for (e = b + 1; e != end; e++) {
+ r = dm_pool_block_is_used(pool->pmd, e, &used);
+ if (r)
+ return r;
+
+ if (used)
+ break;
+ }
+
+ r = issue_discard(tc, b, e, m->bio);
+ if (r)
+ return r;
+
+ b = e;
+ }
+
+ return 0;
}
-static void process_prepared_discard(struct dm_thin_new_mapping *m)
+static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
{
int r;
struct thin_c *tc = m->tc;
+ struct pool *pool = tc->pool;
- r = dm_thin_remove_block(tc->td, m->virt_block);
+ r = dm_thin_remove_range(tc->td, m->virt_begin, m->virt_end);
if (r)
- DMERR_LIMIT("dm_thin_remove_block() failed");
+ metadata_operation_failed(pool, "dm_thin_remove_range", r);
+
+ else if (m->maybe_shared)
+ r = passdown_double_checking_shared_status(m);
+ else
+ r = issue_discard(tc, m->data_block, m->data_block + (m->virt_end - m->virt_begin), m->bio);
- process_prepared_discard_passdown(m);
+ /*
+ * Even if r is set, there could be sub discards in flight that we
+ * need to wait for.
+ */
+ bio_endio(m->bio, r);
+ cell_defer_no_holder(tc, m->cell);
+ mempool_free(m, pool->mapping_pool);
}
static void process_prepared(struct pool *pool, struct list_head *head,
@@ -979,7 +1197,7 @@ static void ll_zero(struct thin_c *tc, struct dm_thin_new_mapping *m,
}
static void remap_and_issue_overwrite(struct thin_c *tc, struct bio *bio,
- dm_block_t data_block,
+ dm_block_t data_begin,
struct dm_thin_new_mapping *m)
{
struct pool *pool = tc->pool;
@@ -989,7 +1207,7 @@ static void remap_and_issue_overwrite(struct thin_c *tc, struct bio *bio,
m->bio = bio;
save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
inc_all_io_entry(pool, bio);
- remap_and_issue(tc, bio, data_block);
+ remap_and_issue(tc, bio, data_begin);
}
/*
@@ -1006,7 +1224,8 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
struct dm_thin_new_mapping *m = get_next_mapping(pool);
m->tc = tc;
- m->virt_block = virt_block;
+ m->virt_begin = virt_block;
+ m->virt_end = virt_block + 1u;
m->data_block = data_dest;
m->cell = cell;
@@ -1085,7 +1304,8 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
atomic_set(&m->prepare_actions, 1); /* no need to quiesce */
m->tc = tc;
- m->virt_block = virt_block;
+ m->virt_begin = virt_block;
+ m->virt_end = virt_block + 1u;
m->data_block = data_block;
m->cell = cell;
@@ -1094,16 +1314,14 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
* zeroing pre-existing data, we can issue the bio immediately.
* Otherwise we use kcopyd to zero the data first.
*/
- if (!pool->pf.zero_new_blocks)
+ if (pool->pf.zero_new_blocks) {
+ if (io_overwrites_block(pool, bio))
+ remap_and_issue_overwrite(tc, bio, data_block, m);
+ else
+ ll_zero(tc, m, data_block * pool->sectors_per_block,
+ (data_block + 1) * pool->sectors_per_block);
+ } else
process_prepared_mapping(m);
-
- else if (io_overwrites_block(pool, bio))
- remap_and_issue_overwrite(tc, bio, data_block, m);
-
- else
- ll_zero(tc, m,
- data_block * pool->sectors_per_block,
- (data_block + 1) * pool->sectors_per_block);
}
static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block,
@@ -1294,99 +1512,149 @@ static void retry_bios_on_resume(struct pool *pool, struct dm_bio_prison_cell *c
retry_on_resume(bio);
}
-static void process_discard_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+static void process_discard_cell_no_passdown(struct thin_c *tc,
+ struct dm_bio_prison_cell *virt_cell)
{
- int r;
- struct bio *bio = cell->holder;
struct pool *pool = tc->pool;
- struct dm_bio_prison_cell *cell2;
- struct dm_cell_key key2;
- dm_block_t block = get_bio_block(tc, bio);
- struct dm_thin_lookup_result lookup_result;
- struct dm_thin_new_mapping *m;
+ struct dm_thin_new_mapping *m = get_next_mapping(pool);
- if (tc->requeue_mode) {
- cell_requeue(pool, cell);
- return;
- }
+ /*
+ * We don't need to lock the data blocks, since there's no
+ * passdown. We only lock data blocks for allocation and breaking sharing.
+ */
+ m->tc = tc;
+ m->virt_begin = virt_cell->key.block_begin;
+ m->virt_end = virt_cell->key.block_end;
+ m->cell = virt_cell;
+ m->bio = virt_cell->holder;
- r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
- switch (r) {
- case 0:
- /*
- * Check nobody is fiddling with this pool block. This can
- * happen if someone's in the process of breaking sharing
- * on this block.
- */
- build_data_key(tc->td, lookup_result.block, &key2);
- if (bio_detain(tc->pool, &key2, bio, &cell2)) {
- cell_defer_no_holder(tc, cell);
- break;
- }
+ if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list))
+ pool->process_prepared_discard(m);
+}
- if (io_overlaps_block(pool, bio)) {
- /*
- * IO may still be going to the destination block. We must
- * quiesce before we can do the removal.
- */
- m = get_next_mapping(pool);
- m->tc = tc;
- m->pass_discard = pool->pf.discard_passdown;
- m->definitely_not_shared = !lookup_result.shared;
- m->virt_block = block;
- m->data_block = lookup_result.block;
- m->cell = cell;
- m->cell2 = cell2;
- m->bio = bio;
-
- if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list))
- pool->process_prepared_discard(m);
+/*
+ * FIXME: DM local hack to defer parent bios's end_io until we
+ * _know_ all chained sub range discard bios have completed.
+ * Will go away once late bio splitting lands upstream!
+ */
+static inline void __bio_inc_remaining(struct bio *bio)
+{
+ bio->bi_flags |= (1 << BIO_CHAIN);
+ smp_mb__before_atomic();
+ atomic_inc(&bio->__bi_remaining);
+}
- } else {
- inc_all_io_entry(pool, bio);
- cell_defer_no_holder(tc, cell);
- cell_defer_no_holder(tc, cell2);
+static void break_up_discard_bio(struct thin_c *tc, dm_block_t begin, dm_block_t end,
+ struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+
+ int r;
+ bool maybe_shared;
+ struct dm_cell_key data_key;
+ struct dm_bio_prison_cell *data_cell;
+ struct dm_thin_new_mapping *m;
+ dm_block_t virt_begin, virt_end, data_begin;
+
+ while (begin != end) {
+ r = ensure_next_mapping(pool);
+ if (r)
+ /* we did our best */
+ return;
+ r = dm_thin_find_mapped_range(tc->td, begin, end, &virt_begin, &virt_end,
+ &data_begin, &maybe_shared);
+ if (r)
/*
- * The DM core makes sure that the discard doesn't span
- * a block boundary. So we submit the discard of a
- * partial block appropriately.
+ * Silently fail, letting any mappings we've
+ * created complete.
*/
- if ((!lookup_result.shared) && pool->pf.discard_passdown)
- remap_and_issue(tc, bio, lookup_result.block);
- else
- bio_endio(bio, 0);
+ break;
+
+ build_key(tc->td, PHYSICAL, data_begin, data_begin + (virt_end - virt_begin), &data_key);
+ if (bio_detain(tc->pool, &data_key, NULL, &data_cell)) {
+ /* contention, we'll give up with this range */
+ begin = virt_end;
+ continue;
}
- break;
- case -ENODATA:
/*
- * It isn't provisioned, just forget it.
+ * IO may still be going to the destination block. We must
+ * quiesce before we can do the removal.
*/
- cell_defer_no_holder(tc, cell);
- bio_endio(bio, 0);
- break;
+ m = get_next_mapping(pool);
+ m->tc = tc;
+ m->maybe_shared = maybe_shared;
+ m->virt_begin = virt_begin;
+ m->virt_end = virt_end;
+ m->data_block = data_begin;
+ m->cell = data_cell;
+ m->bio = bio;
- default:
- DMERR_LIMIT("%s: dm_thin_find_block() failed: error = %d",
- __func__, r);
- cell_defer_no_holder(tc, cell);
- bio_io_error(bio);
- break;
+ /*
+ * The parent bio must not complete before sub discard bios are
+ * chained to it (see __blkdev_issue_discard_async's bio_chain)!
+ *
+ * This per-mapping bi_remaining increment is paired with
+ * the implicit decrement that occurs via bio_endio() in
+ * process_prepared_discard_{passdown,no_passdown}.
+ */
+ __bio_inc_remaining(bio);
+ if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list))
+ pool->process_prepared_discard(m);
+
+ begin = virt_end;
}
}
+static void process_discard_cell_passdown(struct thin_c *tc, struct dm_bio_prison_cell *virt_cell)
+{
+ struct bio *bio = virt_cell->holder;
+ struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
+
+ /*
+ * The virt_cell will only get freed once the origin bio completes.
+ * This means it will remain locked while all the individual
+ * passdown bios are in flight.
+ */
+ h->cell = virt_cell;
+ break_up_discard_bio(tc, virt_cell->key.block_begin, virt_cell->key.block_end, bio);
+
+ /*
+ * We complete the bio now, knowing that the bi_remaining field
+ * will prevent completion until the sub range discards have
+ * completed.
+ */
+ bio_endio(bio, 0);
+}
+
static void process_discard_bio(struct thin_c *tc, struct bio *bio)
{
- struct dm_bio_prison_cell *cell;
- struct dm_cell_key key;
- dm_block_t block = get_bio_block(tc, bio);
+ dm_block_t begin, end;
+ struct dm_cell_key virt_key;
+ struct dm_bio_prison_cell *virt_cell;
- build_virtual_key(tc->td, block, &key);
- if (bio_detain(tc->pool, &key, bio, &cell))
+ get_bio_block_range(tc, bio, &begin, &end);
+ if (begin == end) {
+ /*
+ * The discard covers less than a block.
+ */
+ bio_endio(bio, 0);
+ return;
+ }
+
+ build_key(tc->td, VIRTUAL, begin, end, &virt_key);
+ if (bio_detain(tc->pool, &virt_key, bio, &virt_cell))
+ /*
+ * Potential starvation issue: We're relying on the
+ * fs/application being well behaved, and not trying to
+ * send IO to a region at the same time as discarding it.
+ * If they do this persistently then it's possible this
+ * cell will never be granted.
+ */
return;
- process_discard_cell(tc, cell);
+ tc->pool->process_discard_cell(tc, virt_cell);
}
static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
@@ -2102,6 +2370,24 @@ static void notify_of_pool_mode_change(struct pool *pool, const char *new_mode)
dm_device_name(pool->pool_md), new_mode);
}
+static bool passdown_enabled(struct pool_c *pt)
+{
+ return pt->adjusted_pf.discard_passdown;
+}
+
+static void set_discard_callbacks(struct pool *pool)
+{
+ struct pool_c *pt = pool->ti->private;
+
+ if (passdown_enabled(pt)) {
+ pool->process_discard_cell = process_discard_cell_passdown;
+ pool->process_prepared_discard = process_prepared_discard_passdown;
+ } else {
+ pool->process_discard_cell = process_discard_cell_no_passdown;
+ pool->process_prepared_discard = process_prepared_discard_no_passdown;
+ }
+}
+
static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
{
struct pool_c *pt = pool->ti->private;
@@ -2153,7 +2439,7 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
pool->process_cell = process_cell_read_only;
pool->process_discard_cell = process_cell_success;
pool->process_prepared_mapping = process_prepared_mapping_fail;
- pool->process_prepared_discard = process_prepared_discard_passdown;
+ pool->process_prepared_discard = process_prepared_discard_success;
error_retry_list(pool);
break;
@@ -2172,9 +2458,8 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
pool->process_bio = process_bio_read_only;
pool->process_discard = process_discard_bio;
pool->process_cell = process_cell_read_only;
- pool->process_discard_cell = process_discard_cell;
pool->process_prepared_mapping = process_prepared_mapping;
- pool->process_prepared_discard = process_prepared_discard;
+ set_discard_callbacks(pool);
if (!pool->pf.error_if_no_space && no_space_timeout)
queue_delayed_work(pool->wq, &pool->no_space_timeout, no_space_timeout);
@@ -2187,9 +2472,8 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
pool->process_bio = process_bio;
pool->process_discard = process_discard_bio;
pool->process_cell = process_cell;
- pool->process_discard_cell = process_discard_cell;
pool->process_prepared_mapping = process_prepared_mapping;
- pool->process_prepared_discard = process_prepared_discard;
+ set_discard_callbacks(pool);
break;
}
@@ -2278,6 +2562,7 @@ static void thin_hook_bio(struct thin_c *tc, struct bio *bio)
h->shared_read_entry = NULL;
h->all_io_entry = NULL;
h->overwrite_mapping = NULL;
+ h->cell = NULL;
}
/*
@@ -2425,7 +2710,6 @@ static void disable_passdown_if_not_supported(struct pool_c *pt)
struct pool *pool = pt->pool;
struct block_device *data_bdev = pt->data_dev->bdev;
struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
- sector_t block_size = pool->sectors_per_block << SECTOR_SHIFT;
const char *reason = NULL;
char buf[BDEVNAME_SIZE];
@@ -2438,12 +2722,6 @@ static void disable_passdown_if_not_supported(struct pool_c *pt)
else if (data_limits->max_discard_sectors < pool->sectors_per_block)
reason = "max discard sectors smaller than a block";
- else if (data_limits->discard_granularity > block_size)
- reason = "discard granularity larger than a block";
-
- else if (!is_factor(block_size, data_limits->discard_granularity))
- reason = "discard granularity not a factor of block size";
-
if (reason) {
DMWARN("Data device (%s) %s: Disabling discard passdown.", bdevname(data_bdev, buf), reason);
pt->adjusted_pf.discard_passdown = false;
@@ -3378,7 +3656,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
if (get_pool_mode(pool) >= PM_READ_ONLY) {
DMERR("%s: unable to service pool target messages in READ_ONLY or FAIL mode",
dm_device_name(pool->pool_md));
- return -EINVAL;
+ return -EOPNOTSUPP;
}
if (!strcasecmp(argv[0], "create_thin"))
@@ -3576,24 +3854,6 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
-static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
-{
- struct pool *pool = pt->pool;
- struct queue_limits *data_limits;
-
- limits->max_discard_sectors = pool->sectors_per_block;
-
- /*
- * discard_granularity is just a hint, and not enforced.
- */
- if (pt->adjusted_pf.discard_passdown) {
- data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
- limits->discard_granularity = max(data_limits->discard_granularity,
- pool->sectors_per_block << SECTOR_SHIFT);
- } else
- limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
-}
-
static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct pool_c *pt = ti->private;
@@ -3648,14 +3908,17 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
disable_passdown_if_not_supported(pt);
- set_discard_limits(pt, limits);
+ /*
+ * The pool uses the same discard limits as the underlying data
+ * device. DM core has already set this up.
+ */
}
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 14, 0},
+ .version = {1, 15, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -3814,8 +4077,7 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
if (tc->pool->pf.discard_enabled) {
ti->discards_supported = true;
ti->num_discard_bios = 1;
- /* Discard bios must be split on a block boundary */
- ti->split_discard_bios = true;
+ ti->split_discard_bios = false;
}
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -3902,6 +4164,9 @@ static int thin_endio(struct dm_target *ti, struct bio *bio, int err)
}
}
+ if (h->cell)
+ cell_defer_no_holder(h->tc, h->cell);
+
return 0;
}
@@ -4029,9 +4294,18 @@ static int thin_iterate_devices(struct dm_target *ti,
return 0;
}
+static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct thin_c *tc = ti->private;
+ struct pool *pool = tc->pool;
+
+ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
+ limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */
+}
+
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 14, 0},
+ .version = {1, 15, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
@@ -4043,6 +4317,7 @@ static struct target_type thin_target = {
.status = thin_status,
.merge = thin_merge,
.iterate_devices = thin_iterate_devices,
+ .io_hints = thin_io_hints,
};
/*----------------------------------------------------------------*/
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 66616db..bb9c6a0 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -459,7 +459,7 @@ static void verity_finish_io(struct dm_verity_io *io, int error)
bio->bi_end_io = io->orig_bi_end_io;
bio->bi_private = io->orig_bi_private;
- bio_endio_nodec(bio, error);
+ bio_endio(bio, error);
}
static void verity_work(struct work_struct *w)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 2caf492..2fe0992 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -86,6 +86,9 @@ struct dm_rq_target_io {
struct kthread_work work;
int error;
union map_info info;
+ struct dm_stats_aux stats_aux;
+ unsigned long duration_jiffies;
+ unsigned n_sectors;
};
/*
@@ -990,60 +993,20 @@ static void clone_endio(struct bio *bio, int error)
dec_pending(io, error);
}
-/*
- * Partial completion handling for request-based dm
- */
-static void end_clone_bio(struct bio *clone, int error)
+static struct dm_rq_target_io *tio_from_request(struct request *rq)
{
- struct dm_rq_clone_bio_info *info =
- container_of(clone, struct dm_rq_clone_bio_info, clone);
- struct dm_rq_target_io *tio = info->tio;
- struct bio *bio = info->orig;
- unsigned int nr_bytes = info->orig->bi_iter.bi_size;
-
- bio_put(clone);
-
- if (tio->error)
- /*
- * An error has already been detected on the request.
- * Once error occurred, just let clone->end_io() handle
- * the remainder.
- */
- return;
- else if (error) {
- /*
- * Don't notice the error to the upper layer yet.
- * The error handling decision is made by the target driver,
- * when the request is completed.
- */
- tio->error = error;
- return;
- }
-
- /*
- * I/O for the bio successfully completed.
- * Notice the data completion to the upper layer.
- */
-
- /*
- * bios are processed from the head of the list.
- * So the completing bio should always be rq->bio.
- * If it's not, something wrong is happening.
- */
- if (tio->orig->bio != bio)
- DMERR("bio completion is going in the middle of the request");
-
- /*
- * Update the original request.
- * Do not use blk_end_request() here, because it may complete
- * the original request before the clone, and break the ordering.
- */
- blk_update_request(tio->orig, 0, nr_bytes);
+ return (rq->q->mq_ops ? blk_mq_rq_to_pdu(rq) : rq->special);
}
-static struct dm_rq_target_io *tio_from_request(struct request *rq)
+static void rq_end_stats(struct mapped_device *md, struct request *orig)
{
- return (rq->q->mq_ops ? blk_mq_rq_to_pdu(rq) : rq->special);
+ if (unlikely(dm_stats_used(&md->stats))) {
+ struct dm_rq_target_io *tio = tio_from_request(orig);
+ tio->duration_jiffies = jiffies - tio->duration_jiffies;
+ dm_stats_account_io(&md->stats, orig->cmd_flags, blk_rq_pos(orig),
+ tio->n_sectors, true, tio->duration_jiffies,
+ &tio->stats_aux);
+ }
}
/*
@@ -1087,8 +1050,6 @@ static void free_rq_clone(struct request *clone)
struct dm_rq_target_io *tio = clone->end_io_data;
struct mapped_device *md = tio->md;
- blk_rq_unprep_clone(clone);
-
if (md->type == DM_TYPE_MQ_REQUEST_BASED)
/* stacked on blk-mq queue(s) */
tio->ti->type->release_clone_rq(clone);
@@ -1131,6 +1092,7 @@ static void dm_end_request(struct request *clone, int error)
}
free_rq_clone(clone);
+ rq_end_stats(md, rq);
if (!rq->q->mq_ops)
blk_end_request_all(rq, error);
else
@@ -1166,13 +1128,14 @@ static void old_requeue_request(struct request *rq)
spin_unlock_irqrestore(q->queue_lock, flags);
}
-static void dm_requeue_unmapped_original_request(struct mapped_device *md,
- struct request *rq)
+static void dm_requeue_original_request(struct mapped_device *md,
+ struct request *rq)
{
int rw = rq_data_dir(rq);
dm_unprep_request(rq);
+ rq_end_stats(md, rq);
if (!rq->q->mq_ops)
old_requeue_request(rq);
else {
@@ -1183,13 +1146,6 @@ static void dm_requeue_unmapped_original_request(struct mapped_device *md,
rq_completed(md, rw, false);
}
-static void dm_requeue_unmapped_request(struct request *clone)
-{
- struct dm_rq_target_io *tio = clone->end_io_data;
-
- dm_requeue_unmapped_original_request(tio->md, tio->orig);
-}
-
static void old_stop_queue(struct request_queue *q)
{
unsigned long flags;
@@ -1253,7 +1209,7 @@ static void dm_done(struct request *clone, int error, bool mapped)
return;
else if (r == DM_ENDIO_REQUEUE)
/* The target wants to requeue the I/O */
- dm_requeue_unmapped_request(clone);
+ dm_requeue_original_request(tio->md, tio->orig);
else {
DMWARN("unimplemented target endio return value: %d", r);
BUG();
@@ -1271,6 +1227,7 @@ static void dm_softirq_done(struct request *rq)
int rw;
if (!clone) {
+ rq_end_stats(tio->md, rq);
rw = rq_data_dir(rq);
if (!rq->q->mq_ops) {
blk_end_request_all(rq, tio->error);
@@ -1827,39 +1784,13 @@ static void dm_dispatch_clone_request(struct request *clone, struct request *rq)
dm_complete_request(rq, r);
}
-static int dm_rq_bio_constructor(struct bio *bio, struct bio *bio_orig,
- void *data)
+static void setup_clone(struct request *clone, struct request *rq,
+ struct dm_rq_target_io *tio)
{
- struct dm_rq_target_io *tio = data;
- struct dm_rq_clone_bio_info *info =
- container_of(bio, struct dm_rq_clone_bio_info, clone);
-
- info->orig = bio_orig;
- info->tio = tio;
- bio->bi_end_io = end_clone_bio;
-
- return 0;
-}
-
-static int setup_clone(struct request *clone, struct request *rq,
- struct dm_rq_target_io *tio, gfp_t gfp_mask)
-{
- int r;
-
- r = blk_rq_prep_clone(clone, rq, tio->md->bs, gfp_mask,
- dm_rq_bio_constructor, tio);
- if (r)
- return r;
-
- clone->cmd = rq->cmd;
- clone->cmd_len = rq->cmd_len;
- clone->sense = rq->sense;
+ blk_rq_prep_clone(clone, rq);
clone->end_io = end_clone_request;
clone->end_io_data = tio;
-
tio->clone = clone;
-
- return 0;
}
static struct request *clone_rq(struct request *rq, struct mapped_device *md,
@@ -1880,12 +1811,7 @@ static struct request *clone_rq(struct request *rq, struct mapped_device *md,
clone = tio->clone;
blk_rq_init(NULL, clone);
- if (setup_clone(clone, rq, tio, gfp_mask)) {
- /* -ENOMEM */
- if (alloc_clone)
- free_clone_request(md, clone);
- return NULL;
- }
+ setup_clone(clone, rq, tio);
return clone;
}
@@ -1979,11 +1905,7 @@ static int map_request(struct dm_rq_target_io *tio, struct request *rq,
}
if (r != DM_MAPIO_REMAPPED)
return r;
- if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
- /* -ENOMEM */
- ti->type->release_clone_rq(clone);
- return DM_MAPIO_REQUEUE;
- }
+ setup_clone(clone, rq, tio);
}
switch (r) {
@@ -1998,7 +1920,7 @@ static int map_request(struct dm_rq_target_io *tio, struct request *rq,
break;
case DM_MAPIO_REQUEUE:
/* The target wants to requeue the I/O */
- dm_requeue_unmapped_request(clone);
+ dm_requeue_original_request(md, tio->orig);
break;
default:
if (r > 0) {
@@ -2021,7 +1943,7 @@ static void map_tio_request(struct kthread_work *work)
struct mapped_device *md = tio->md;
if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE)
- dm_requeue_unmapped_original_request(md, rq);
+ dm_requeue_original_request(md, rq);
}
static void dm_start_request(struct mapped_device *md, struct request *orig)
@@ -2038,6 +1960,14 @@ static void dm_start_request(struct mapped_device *md, struct request *orig)
md->last_rq_start_time = ktime_get();
}
+ if (unlikely(dm_stats_used(&md->stats))) {
+ struct dm_rq_target_io *tio = tio_from_request(orig);
+ tio->duration_jiffies = jiffies;
+ tio->n_sectors = blk_rq_sectors(orig);
+ dm_stats_account_io(&md->stats, orig->cmd_flags, blk_rq_pos(orig),
+ tio->n_sectors, false, 0, &tio->stats_aux);
+ }
+
/*
* Hold the md reference here for the in-flight I/O.
* We can't rely on the reference count by device opener,
@@ -2168,7 +2098,7 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
* the query about congestion status of request_queue
*/
if (dm_request_based(md))
- r = md->queue->backing_dev_info.state &
+ r = md->queue->backing_dev_info.wb.state &
bdi_bits;
else
r = dm_table_any_congested(map, bdi_bits);
@@ -2261,6 +2191,40 @@ static void dm_init_old_md_queue(struct mapped_device *md)
blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
}
+static void cleanup_mapped_device(struct mapped_device *md)
+{
+ cleanup_srcu_struct(&md->io_barrier);
+
+ if (md->wq)
+ destroy_workqueue(md->wq);
+ if (md->kworker_task)
+ kthread_stop(md->kworker_task);
+ if (md->io_pool)
+ mempool_destroy(md->io_pool);
+ if (md->rq_pool)
+ mempool_destroy(md->rq_pool);
+ if (md->bs)
+ bioset_free(md->bs);
+
+ if (md->disk) {
+ spin_lock(&_minor_lock);
+ md->disk->private_data = NULL;
+ spin_unlock(&_minor_lock);
+ if (blk_get_integrity(md->disk))
+ blk_integrity_unregister(md->disk);
+ del_gendisk(md->disk);
+ put_disk(md->disk);
+ }
+
+ if (md->queue)
+ blk_cleanup_queue(md->queue);
+
+ if (md->bdev) {
+ bdput(md->bdev);
+ md->bdev = NULL;
+ }
+}
+
/*
* Allocate and initialise a blank device with a given minor.
*/
@@ -2306,13 +2270,13 @@ static struct mapped_device *alloc_dev(int minor)
md->queue = blk_alloc_queue(GFP_KERNEL);
if (!md->queue)
- goto bad_queue;
+ goto bad;
dm_init_md_queue(md);
md->disk = alloc_disk(1);
if (!md->disk)
- goto bad_disk;
+ goto bad;
atomic_set(&md->pending[0], 0);
atomic_set(&md->pending[1], 0);
@@ -2333,11 +2297,11 @@ static struct mapped_device *alloc_dev(int minor)
md->wq = alloc_workqueue("kdmflush", WQ_MEM_RECLAIM, 0);
if (!md->wq)
- goto bad_thread;
+ goto bad;
md->bdev = bdget_disk(md->disk, 0);
if (!md->bdev)
- goto bad_bdev;
+ goto bad;
bio_init(&md->flush_bio);
md->flush_bio.bi_bdev = md->bdev;
@@ -2354,15 +2318,8 @@ static struct mapped_device *alloc_dev(int minor)
return md;
-bad_bdev:
- destroy_workqueue(md->wq);
-bad_thread:
- del_gendisk(md->disk);
- put_disk(md->disk);
-bad_disk:
- blk_cleanup_queue(md->queue);
-bad_queue:
- cleanup_srcu_struct(&md->io_barrier);
+bad:
+ cleanup_mapped_device(md);
bad_io_barrier:
free_minor(minor);
bad_minor:
@@ -2379,62 +2336,55 @@ static void free_dev(struct mapped_device *md)
int minor = MINOR(disk_devt(md->disk));
unlock_fs(md);
- destroy_workqueue(md->wq);
- if (md->kworker_task)
- kthread_stop(md->kworker_task);
- if (md->io_pool)
- mempool_destroy(md->io_pool);
- if (md->rq_pool)
- mempool_destroy(md->rq_pool);
- if (md->bs)
- bioset_free(md->bs);
+ cleanup_mapped_device(md);
+ if (md->use_blk_mq)
+ blk_mq_free_tag_set(&md->tag_set);
- cleanup_srcu_struct(&md->io_barrier);
free_table_devices(&md->table_devices);
dm_stats_cleanup(&md->stats);
-
- spin_lock(&_minor_lock);
- md->disk->private_data = NULL;
- spin_unlock(&_minor_lock);
- if (blk_get_integrity(md->disk))
- blk_integrity_unregister(md->disk);
- del_gendisk(md->disk);
- put_disk(md->disk);
- blk_cleanup_queue(md->queue);
- if (md->use_blk_mq)
- blk_mq_free_tag_set(&md->tag_set);
- bdput(md->bdev);
free_minor(minor);
module_put(THIS_MODULE);
kfree(md);
}
+static unsigned filter_md_type(unsigned type, struct mapped_device *md)
+{
+ if (type == DM_TYPE_BIO_BASED)
+ return type;
+
+ return !md->use_blk_mq ? DM_TYPE_REQUEST_BASED : DM_TYPE_MQ_REQUEST_BASED;
+}
+
static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
{
struct dm_md_mempools *p = dm_table_get_md_mempools(t);
- if (md->bs) {
- /* The md already has necessary mempools. */
- if (dm_table_get_type(t) == DM_TYPE_BIO_BASED) {
+ switch (filter_md_type(dm_table_get_type(t), md)) {
+ case DM_TYPE_BIO_BASED:
+ if (md->bs && md->io_pool) {
/*
+ * This bio-based md already has necessary mempools.
* Reload bioset because front_pad may have changed
* because a different table was loaded.
*/
bioset_free(md->bs);
md->bs = p->bs;
p->bs = NULL;
+ goto out;
}
- /*
- * There's no need to reload with request-based dm
- * because the size of front_pad doesn't change.
- * Note for future: If you are to reload bioset,
- * prep-ed requests in the queue may refer
- * to bio from the old bioset, so you must walk
- * through the queue to unprep.
- */
- goto out;
+ break;
+ case DM_TYPE_REQUEST_BASED:
+ if (md->rq_pool && md->io_pool)
+ /*
+ * This request-based md already has necessary mempools.
+ */
+ goto out;
+ break;
+ case DM_TYPE_MQ_REQUEST_BASED:
+ BUG_ON(p); /* No mempools needed */
+ return;
}
BUG_ON(!p || md->io_pool || md->rq_pool || md->bs);
@@ -2445,7 +2395,6 @@ static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
p->rq_pool = NULL;
md->bs = p->bs;
p->bs = NULL;
-
out:
/* mempool bind completed, no longer need any mempools in the table */
dm_table_free_md_mempools(t);
@@ -2765,6 +2714,7 @@ static int dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
/* Direct call is fine since .queue_rq allows allocations */
if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE) {
/* Undo dm_start_request() before requeuing */
+ rq_end_stats(md, rq);
rq_completed(md, rq_data_dir(rq), false);
return BLK_MQ_RQ_QUEUE_BUSY;
}
@@ -2824,14 +2774,6 @@ out_tag_set:
return err;
}
-static unsigned filter_md_type(unsigned type, struct mapped_device *md)
-{
- if (type == DM_TYPE_BIO_BASED)
- return type;
-
- return !md->use_blk_mq ? DM_TYPE_REQUEST_BASED : DM_TYPE_MQ_REQUEST_BASED;
-}
-
/*
* Setup the DM device's queue based on md's type
*/
@@ -3544,48 +3486,23 @@ int dm_noflush_suspending(struct dm_target *ti)
}
EXPORT_SYMBOL_GPL(dm_noflush_suspending);
-struct dm_md_mempools *dm_alloc_md_mempools(struct mapped_device *md, unsigned type,
- unsigned integrity, unsigned per_bio_data_size)
+struct dm_md_mempools *dm_alloc_bio_mempools(unsigned integrity,
+ unsigned per_bio_data_size)
{
- struct dm_md_mempools *pools = kzalloc(sizeof(*pools), GFP_KERNEL);
- struct kmem_cache *cachep = NULL;
- unsigned int pool_size = 0;
+ struct dm_md_mempools *pools;
+ unsigned int pool_size = dm_get_reserved_bio_based_ios();
unsigned int front_pad;
+ pools = kzalloc(sizeof(*pools), GFP_KERNEL);
if (!pools)
- return NULL;
-
- type = filter_md_type(type, md);
+ return ERR_PTR(-ENOMEM);
- switch (type) {
- case DM_TYPE_BIO_BASED:
- cachep = _io_cache;
- pool_size = dm_get_reserved_bio_based_ios();
- front_pad = roundup(per_bio_data_size, __alignof__(struct dm_target_io)) + offsetof(struct dm_target_io, clone);
- break;
- case DM_TYPE_REQUEST_BASED:
- cachep = _rq_tio_cache;
- pool_size = dm_get_reserved_rq_based_ios();
- pools->rq_pool = mempool_create_slab_pool(pool_size, _rq_cache);
- if (!pools->rq_pool)
- goto out;
- /* fall through to setup remaining rq-based pools */
- case DM_TYPE_MQ_REQUEST_BASED:
- if (!pool_size)
- pool_size = dm_get_reserved_rq_based_ios();
- front_pad = offsetof(struct dm_rq_clone_bio_info, clone);
- /* per_bio_data_size is not used. See __bind_mempools(). */
- WARN_ON(per_bio_data_size != 0);
- break;
- default:
- BUG();
- }
+ front_pad = roundup(per_bio_data_size, __alignof__(struct dm_target_io)) +
+ offsetof(struct dm_target_io, clone);
- if (cachep) {
- pools->io_pool = mempool_create_slab_pool(pool_size, cachep);
- if (!pools->io_pool)
- goto out;
- }
+ pools->io_pool = mempool_create_slab_pool(pool_size, _io_cache);
+ if (!pools->io_pool)
+ goto out;
pools->bs = bioset_create_nobvec(pool_size, front_pad);
if (!pools->bs)
@@ -3595,11 +3512,37 @@ struct dm_md_mempools *dm_alloc_md_mempools(struct mapped_device *md, unsigned t
goto out;
return pools;
-
out:
dm_free_md_mempools(pools);
+ return ERR_PTR(-ENOMEM);
+}
- return NULL;
+struct dm_md_mempools *dm_alloc_rq_mempools(struct mapped_device *md,
+ unsigned type)
+{
+ unsigned int pool_size;
+ struct dm_md_mempools *pools;
+
+ if (filter_md_type(type, md) == DM_TYPE_MQ_REQUEST_BASED)
+ return NULL; /* No mempools needed */
+
+ pool_size = dm_get_reserved_rq_based_ios();
+ pools = kzalloc(sizeof(*pools), GFP_KERNEL);
+ if (!pools)
+ return ERR_PTR(-ENOMEM);
+
+ pools->rq_pool = mempool_create_slab_pool(pool_size, _rq_cache);
+ if (!pools->rq_pool)
+ goto out;
+
+ pools->io_pool = mempool_create_slab_pool(pool_size, _rq_tio_cache);
+ if (!pools->io_pool)
+ goto out;
+
+ return pools;
+out:
+ dm_free_md_mempools(pools);
+ return ERR_PTR(-ENOMEM);
}
void dm_free_md_mempools(struct dm_md_mempools *pools)
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 6123c2b..7fff744 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -14,6 +14,7 @@
#include <linux/device-mapper.h>
#include <linux/list.h>
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/hdreg.h>
#include <linux/completion.h>
#include <linux/kobject.h>
@@ -222,8 +223,9 @@ void dm_kcopyd_exit(void);
/*
* Mempool operations
*/
-struct dm_md_mempools *dm_alloc_md_mempools(struct mapped_device *md, unsigned type,
- unsigned integrity, unsigned per_bio_data_size);
+struct dm_md_mempools *dm_alloc_bio_mempools(unsigned integrity,
+ unsigned per_bio_data_size);
+struct dm_md_mempools *dm_alloc_rq_mempools(struct mapped_device *md, unsigned type);
void dm_free_md_mempools(struct dm_md_mempools *pools);
/*
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 4046a6c..7da6e9c 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -16,6 +16,7 @@
#define _MD_MD_H
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/mm.h>
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 087411c..4d6c9b6 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -609,6 +609,12 @@ void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
dm_bufio_prefetch(bm->bufio, b, 1);
}
+bool dm_bm_is_read_only(struct dm_block_manager *bm)
+{
+ return bm->read_only;
+}
+EXPORT_SYMBOL_GPL(dm_bm_is_read_only);
+
void dm_bm_set_read_only(struct dm_block_manager *bm)
{
bm->read_only = true;
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
index 1b95dfc..84330f5 100644
--- a/drivers/md/persistent-data/dm-block-manager.h
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -123,6 +123,7 @@ void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b);
* Additionally you should not use dm_bm_unlock_move, however no error will
* be returned if you do.
*/
+bool dm_bm_is_read_only(struct dm_block_manager *bm);
void dm_bm_set_read_only(struct dm_block_manager *bm);
void dm_bm_set_read_write(struct dm_block_manager *bm);
diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c
index b88757c..e04cfd2 100644
--- a/drivers/md/persistent-data/dm-btree-remove.c
+++ b/drivers/md/persistent-data/dm-btree-remove.c
@@ -590,3 +590,130 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
return r;
}
EXPORT_SYMBOL_GPL(dm_btree_remove);
+
+/*----------------------------------------------------------------*/
+
+static int remove_nearest(struct shadow_spine *s, struct dm_btree_info *info,
+ struct dm_btree_value_type *vt, dm_block_t root,
+ uint64_t key, int *index)
+{
+ int i = *index, r;
+ struct btree_node *n;
+
+ for (;;) {
+ r = shadow_step(s, root, vt);
+ if (r < 0)
+ break;
+
+ /*
+ * We have to patch up the parent node, ugly, but I don't
+ * see a way to do this automatically as part of the spine
+ * op.
+ */
+ if (shadow_has_parent(s)) {
+ __le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
+ memcpy(value_ptr(dm_block_data(shadow_parent(s)), i),
+ &location, sizeof(__le64));
+ }
+
+ n = dm_block_data(shadow_current(s));
+
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE) {
+ *index = lower_bound(n, key);
+ return 0;
+ }
+
+ r = rebalance_children(s, info, vt, key);
+ if (r)
+ break;
+
+ n = dm_block_data(shadow_current(s));
+ if (le32_to_cpu(n->header.flags) & LEAF_NODE) {
+ *index = lower_bound(n, key);
+ return 0;
+ }
+
+ i = lower_bound(n, key);
+
+ /*
+ * We know the key is present, or else
+ * rebalance_children would have returned
+ * -ENODATA
+ */
+ root = value64(n, i);
+ }
+
+ return r;
+}
+
+static int remove_one(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, uint64_t end_key,
+ dm_block_t *new_root, unsigned *nr_removed)
+{
+ unsigned level, last_level = info->levels - 1;
+ int index = 0, r = 0;
+ struct shadow_spine spine;
+ struct btree_node *n;
+ uint64_t k;
+
+ init_shadow_spine(&spine, info);
+ for (level = 0; level < last_level; level++) {
+ r = remove_raw(&spine, info, &le64_type,
+ root, keys[level], (unsigned *) &index);
+ if (r < 0)
+ goto out;
+
+ n = dm_block_data(shadow_current(&spine));
+ root = value64(n, index);
+ }
+
+ r = remove_nearest(&spine, info, &info->value_type,
+ root, keys[last_level], &index);
+ if (r < 0)
+ goto out;
+
+ n = dm_block_data(shadow_current(&spine));
+
+ if (index < 0)
+ index = 0;
+
+ if (index >= le32_to_cpu(n->header.nr_entries)) {
+ r = -ENODATA;
+ goto out;
+ }
+
+ k = le64_to_cpu(n->keys[index]);
+ if (k >= keys[last_level] && k < end_key) {
+ if (info->value_type.dec)
+ info->value_type.dec(info->value_type.context,
+ value_ptr(n, index));
+
+ delete_at(n, index);
+
+ } else
+ r = -ENODATA;
+
+out:
+ *new_root = shadow_root(&spine);
+ exit_shadow_spine(&spine);
+
+ return r;
+}
+
+int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *first_key, uint64_t end_key,
+ dm_block_t *new_root, unsigned *nr_removed)
+{
+ int r;
+
+ *nr_removed = 0;
+ do {
+ r = remove_one(info, root, first_key, end_key, &root, nr_removed);
+ if (!r)
+ (*nr_removed)++;
+ } while (!r);
+
+ *new_root = root;
+ return r == -ENODATA ? 0 : r;
+}
+EXPORT_SYMBOL_GPL(dm_btree_remove_leaves);
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h
index dacfc34..11d8cf7 100644
--- a/drivers/md/persistent-data/dm-btree.h
+++ b/drivers/md/persistent-data/dm-btree.h
@@ -135,6 +135,15 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, dm_block_t *new_root);
/*
+ * Removes values between 'keys' and keys2, where keys2 is keys with the
+ * final key replaced with 'end_key'. 'end_key' is the one-past-the-end
+ * value. 'keys' may be altered.
+ */
+int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, uint64_t end_key,
+ dm_block_t *new_root, unsigned *nr_removed);
+
+/*
* Returns < 0 on failure. Otherwise the number of key entries that have
* been filled out. Remember trees can have zero entries, and as such have
* no lowest key.
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index e8a9042..5309129 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -204,6 +204,27 @@ static void in(struct sm_metadata *smm)
smm->recursion_count++;
}
+static int apply_bops(struct sm_metadata *smm)
+{
+ int r = 0;
+
+ while (!brb_empty(&smm->uncommitted)) {
+ struct block_op bop;
+
+ r = brb_pop(&smm->uncommitted, &bop);
+ if (r) {
+ DMERR("bug in bop ring buffer");
+ break;
+ }
+
+ r = commit_bop(smm, &bop);
+ if (r)
+ break;
+ }
+
+ return r;
+}
+
static int out(struct sm_metadata *smm)
{
int r = 0;
@@ -216,21 +237,8 @@ static int out(struct sm_metadata *smm)
return -ENOMEM;
}
- if (smm->recursion_count == 1) {
- while (!brb_empty(&smm->uncommitted)) {
- struct block_op bop;
-
- r = brb_pop(&smm->uncommitted, &bop);
- if (r) {
- DMERR("bug in bop ring buffer");
- break;
- }
-
- r = commit_bop(smm, &bop);
- if (r)
- break;
- }
- }
+ if (smm->recursion_count == 1)
+ apply_bops(smm);
smm->recursion_count--;
@@ -704,6 +712,12 @@ static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
}
old_len = smm->begin;
+ r = apply_bops(smm);
+ if (r) {
+ DMERR("%s: apply_bops failed", __func__);
+ goto out;
+ }
+
r = sm_ll_commit(&smm->ll);
if (r)
goto out;
@@ -773,6 +787,12 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
if (r)
return r;
+ r = apply_bops(smm);
+ if (r) {
+ DMERR("%s: apply_bops failed", __func__);
+ return r;
+ }
+
return sm_metadata_commit(sm);
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 9157a29..f80f1af 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -745,7 +745,7 @@ static int raid1_congested(struct mddev *mddev, int bits)
struct r1conf *conf = mddev->private;
int i, ret = 0;
- if ((bits & (1 << BDI_async_congested)) &&
+ if ((bits & (1 << WB_async_congested)) &&
conf->pending_count >= max_queued_requests)
return 1;
@@ -760,7 +760,7 @@ static int raid1_congested(struct mddev *mddev, int bits)
/* Note the '|| 1' - when read_balance prefers
* non-congested targets, it can be removed
*/
- if ((bits & (1<<BDI_async_congested)) || 1)
+ if ((bits & (1 << WB_async_congested)) || 1)
ret |= bdi_congested(&q->backing_dev_info, bits);
else
ret &= bdi_congested(&q->backing_dev_info, bits);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index f55c3f3..188d8e9 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -914,7 +914,7 @@ static int raid10_congested(struct mddev *mddev, int bits)
struct r10conf *conf = mddev->private;
int i, ret = 0;
- if ((bits & (1 << BDI_async_congested)) &&
+ if ((bits & (1 << WB_async_congested)) &&
conf->pending_count >= max_queued_requests)
return 1;
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 1570992..3ef3d6c 100644
--- a/drivers/media/Kconfig
+++ b/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/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig
index a8c6cdf..e593638 100644
--- a/drivers/media/common/b2c2/Kconfig
+++ b/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/drivers/media/common/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h
index 437912e..2b2460e 100644
--- a/drivers/media/common/b2c2/flexcop-common.h
+++ b/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/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c
index 7e14e90..9c59f43 100644
--- a/drivers/media/common/b2c2/flexcop-fe-tuner.c
+++ b/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/drivers/media/common/b2c2/flexcop-hw-filter.c b/drivers/media/common/b2c2/flexcop-hw-filter.c
index 77e4547..8220257 100644
--- a/drivers/media/common/b2c2/flexcop-hw-filter.c
+++ b/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/drivers/media/common/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c
index f06f3a9..b8eff23 100644
--- a/drivers/media/common/b2c2/flexcop-misc.c
+++ b/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/drivers/media/common/b2c2/flexcop-reg.h b/drivers/media/common/b2c2/flexcop-reg.h
index dc4528d..835c54d 100644
--- a/drivers/media/common/b2c2/flexcop-reg.h
+++ b/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/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index eb8bd68..4cc39e4 100644
--- a/drivers/media/common/siano/smscoreapi.h
+++ b/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/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
index 367b8e7..f4305ae 100644
--- a/drivers/media/common/siano/smsdvb-main.c
+++ b/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/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h
index ae36d0a..b15754d 100644
--- a/drivers/media/common/siano/smsdvb.h
+++ b/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/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index 1d60d20..41f2a39 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/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/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index 882ca41..842b9c8 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -40,6 +40,7 @@
#include <linux/freezer.h>
#include <linux/jiffies.h>
#include <linux/kthread.h>
+#include <linux/ktime.h>
#include <asm/processor.h>
#include "dvb_frontend.h"
@@ -110,7 +111,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 +199,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 +431,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 +692,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 +891,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 +2197,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)
@@ -2341,7 +2322,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
}
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 +2384,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 +2398,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 +2407,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 +2417,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 +2427,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 +2449,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 +2466,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 +2480,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/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h
index 816269e..4816947 100644
--- a/drivers/media/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb-core/dvb_frontend.h
@@ -279,7 +279,7 @@ 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 +289,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 +298,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);
@@ -338,24 +340,24 @@ struct dtv_frontend_properties {
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 +370,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];
@@ -439,7 +441,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/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 97c151d..0d35f58 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -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
@@ -232,7 +240,8 @@ config DVB_SI21XX
config DVB_TS2020
tristate "Montage Tehnology TS2020 based tuners"
- depends on DVB_CORE && I2C
+ depends on DVB_CORE
+ 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.
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 23d399b..ebab1b8 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -83,6 +83,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
diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
index 780da58..97ecbe0 100644
--- a/drivers/media/dvb-frontends/a8293.c
+++ b/drivers/media/dvb-frontends/a8293.c
@@ -22,8 +22,9 @@
#include "a8293.h"
struct a8293_priv {
+ u8 i2c_addr;
struct i2c_adapter *i2c;
- const struct a8293_config *cfg;
+ struct i2c_client *client;
u8 reg[2];
};
@@ -32,7 +33,7 @@ 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,
+ .addr = priv->i2c_addr,
.len = len,
.buf = val,
}
@@ -66,7 +67,7 @@ static int a8293_rd(struct a8293_priv *priv, u8 *val, int len)
}
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;
int ret;
@@ -128,7 +129,7 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
/* setup the priv */
priv->i2c = i2c;
- priv->cfg = cfg;
+ priv->i2c_addr = cfg->i2c_addr;
fe->sec_priv = priv;
/* check if the SEC is there */
@@ -164,6 +165,86 @@ err:
}
EXPORT_SYMBOL(a8293_attach);
+static int a8293_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct a8293_priv *dev;
+ struct a8293_platform_data *pdata = client->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+ int ret;
+ u8 buf[2];
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->client = client;
+ dev->i2c = client->adapter;
+ dev->i2c_addr = client->addr;
+
+ /* check if the SEC is there */
+ ret = a8293_rd(dev, buf, 2);
+ if (ret)
+ goto err_kfree;
+
+ /* ENB=0 */
+ dev->reg[0] = 0x10;
+ ret = a8293_wr(dev, &dev->reg[0], 1);
+ if (ret)
+ goto err_kfree;
+
+ /* TMODE=0, TGATE=1 */
+ dev->reg[1] = 0x82;
+ ret = a8293_wr(dev, &dev->reg[1], 1);
+ if (ret)
+ goto err_kfree;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = a8293_set_voltage;
+
+ fe->sec_priv = dev;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n");
+ return 0;
+err_kfree:
+ kfree(dev);
+err:
+ 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;
+}
+
+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 = {
+ .owner = THIS_MODULE,
+ .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 <crope@iki.fi>");
MODULE_DESCRIPTION("Allegro A8293 SEC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h
index 5f04119..aff3653 100644
--- a/drivers/media/dvb-frontends/a8293.h
+++ b/drivers/media/dvb-frontends/a8293.h
@@ -21,8 +21,23 @@
#ifndef A8293_H
#define A8293_H
+#include "dvb_frontend.h"
#include <linux/kconfig.h>
+/*
+ * I2C address
+ * 0x08, 0x09, 0x0a, 0x0b
+ */
+
+/**
+ * struct a8293_platform_data - Platform data for the a8293 driver
+ * @dvb_frontend: DVB frontend.
+ */
+struct a8293_platform_data {
+ struct dvb_frontend *dvb_frontend;
+};
+
+
struct a8293_config {
u8 i2c_addr;
};
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
index 8001690..e23197d 100644
--- a/drivers/media/dvb-frontends/af9013.c
+++ b/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;
@@ -605,6 +605,10 @@ static int af9013_set_frontend(struct dvb_frontend *fe)
}
}
+ /* Return an error if can't find bandwidth or the right clock */
+ if (i == ARRAY_SIZE(coeff_lut))
+ return -EINVAL;
+
ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val,
sizeof(coeff_lut[i].val));
}
@@ -979,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/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
index 82ce47b..59018af 100644
--- a/drivers/media/dvb-frontends/af9033.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c
index 4936658..544c5f6 100644
--- a/drivers/media/dvb-frontends/as102_fe.c
+++ b/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/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c
index 4e11dc4..8fe552e 100644
--- a/drivers/media/dvb-frontends/atbm8830.c
+++ b/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/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
index 5d06c99..b744a3f 100644
--- a/drivers/media/dvb-frontends/au8522_dig.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h
index b8aca1c..951b384 100644
--- a/drivers/media/dvb-frontends/au8522_priv.h
+++ b/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/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c
index 638c7aa..d30275f 100644
--- a/drivers/media/dvb-frontends/bcm3510.c
+++ b/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/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c
index 8656326..fd033cc 100644
--- a/drivers/media/dvb-frontends/cx22700.c
+++ b/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/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c
index edc8eaf..d2d06dc 100644
--- a/drivers/media/dvb-frontends/cx22702.c
+++ b/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/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c
index 7b510f2..cb36475 100644
--- a/drivers/media/dvb-frontends/cx24110.c
+++ b/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/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c
index 2916d7c..8814f36 100644
--- a/drivers/media/dvb-frontends/cx24116.c
+++ b/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;
@@ -963,6 +965,10 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
struct cx24116_state *state = fe->demodulator_priv;
int i, ret;
+ /* Validate length */
+ if (d->msg_len > sizeof(d->msg))
+ return -EINVAL;
+
/* Dump DiSEqC message */
if (debug) {
printk(KERN_INFO "cx24116: %s(", __func__);
@@ -974,10 +980,6 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
printk(") toneburst=%d\n", toneburst);
}
- /* Validate length */
- if (d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
- return -EINVAL;
-
/* DiSEqC message */
for (i = 0; i < d->msg_len; i++)
state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
@@ -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/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
index acb965c..5f77bc8 100644
--- a/drivers/media/dvb-frontends/cx24117.c
+++ b/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;
@@ -1043,7 +1045,7 @@ static int cx24117_send_diseqc_msg(struct dvb_frontend *fe,
dev_dbg(&state->priv->i2c->dev, ")\n");
/* Validate length */
- if (d->msg_len > 15)
+ if (d->msg_len > sizeof(d->msg))
return -EINVAL;
/* DiSEqC message */
@@ -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/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c
new file mode 100644
index 0000000..3b0ef52
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24120.c
@@ -0,0 +1,1595 @@
+/*
+ Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
+ Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
+ Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
+ Copyright (C) 2015 Jemma Denson <jdenson@gmail.com>
+ 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 <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#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 = &reg
+ }, {
+ .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/drivers/media/dvb-frontends/cx24120.h b/drivers/media/dvb-frontends/cx24120.h
new file mode 100644
index 0000000..f097042
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24120.h
@@ -0,0 +1,58 @@
+/*
+ * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver
+ *
+ * Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
+ * Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
+ * Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
+ * Copyright (C) 2015 Jemma Denson <jdenson@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 CX24120_H
+#define CX24120_H
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+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/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c
index 7975c660..e18cf9e 100644
--- a/drivers/media/dvb-frontends/cx24123.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h
index 758aee5..975f3c9 100644
--- a/drivers/media/dvb-frontends/cx24123.h
+++ b/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/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c
index 72b0e2db..42fad6a 100644
--- a/drivers/media/dvb-frontends/cxd2820r_c.c
+++ b/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/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index 490e090..def6d21 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/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/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h
index 4b42895..a0d53f0 100644
--- a/drivers/media/dvb-frontends/cxd2820r_priv.h
+++ b/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/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c
index 008cb2a..21abf1b 100644
--- a/drivers/media/dvb-frontends/cxd2820r_t.c
+++ b/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/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c
index 35fe364..4e028b4 100644
--- a/drivers/media/dvb-frontends/cxd2820r_t2.c
+++ b/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/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c
index 3b024bf..0b8fb5d 100644
--- a/drivers/media/dvb-frontends/dib0070.c
+++ b/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/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index 68e2af2..47cb722 100644
--- a/drivers/media/dvb-frontends/dib0090.c
+++ b/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/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c
index af91e0c..7a61172 100644
--- a/drivers/media/dvb-frontends/dib3000mb.c
+++ b/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/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c
index ffad181..583d6b7 100644
--- a/drivers/media/dvb-frontends/dib3000mc.c
+++ b/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/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c
index dcb9a15..35eb71f 100644
--- a/drivers/media/dvb-frontends/dib7000m.c
+++ b/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/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index c505d69..33be5d6 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/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/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 8c6663b..94c2627 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/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/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h
index 780c37b..2b8b4b1 100644
--- a/drivers/media/dvb-frontends/dib8000.h
+++ b/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/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
index f75dec4..8f92aca 100644
--- a/drivers/media/dvb-frontends/dib9000.c
+++ b/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/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 2bfa7a4..b28b578 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/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/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index 687e893..34b9441 100644
--- a/drivers/media/dvb-frontends/drxd_hard.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index d46cf5f..b975da0 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/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/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
index bae9c71..9ed88e0 100644
--- a/drivers/media/dvb-frontends/drxk_hard.h
+++ b/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/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 9d0d034..e8fc032 100644
--- a/drivers/media/dvb-frontends/ds3000.c
+++ b/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/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c
index d5acc30..14e996d 100644
--- a/drivers/media/dvb-frontends/dvb_dummy_fe.c
+++ b/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/drivers/media/dvb-frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c
index 9d42480..c9012e6 100644
--- a/drivers/media/dvb-frontends/ec100.c
+++ b/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/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c
index 67c8e6d..40e359f 100644
--- a/drivers/media/dvb-frontends/hd29l2.c
+++ b/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/drivers/media/dvb-frontends/hd29l2_priv.h b/drivers/media/dvb-frontends/hd29l2_priv.h
index 4d571a2..6dc225c 100644
--- a/drivers/media/dvb-frontends/hd29l2_priv.h
+++ b/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/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c
index 0c642a5..b46450a 100644
--- a/drivers/media/dvb-frontends/isl6405.c
+++ b/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/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c
index c77002f..3a4d460 100644
--- a/drivers/media/dvb-frontends/isl6421.c
+++ b/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/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c
index ddf866c..0977871 100644
--- a/drivers/media/dvb-frontends/l64781.c
+++ b/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/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c
index 99efeba..7880f71 100644
--- a/drivers/media/dvb-frontends/lg2160.c
+++ b/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/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c
index d08570a..4712186 100644
--- a/drivers/media/dvb-frontends/lgdt3305.c
+++ b/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/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index d9a2b0e..721fbc0 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/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], &regval1[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/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index 2e1a618..cf3cc20 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/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/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c
index 416cce3..7bbb2c1 100644
--- a/drivers/media/dvb-frontends/lgs8gl5.c
+++ b/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/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c
index 3c92f36..e2c191c 100644
--- a/drivers/media/dvb-frontends/lgs8gxx.c
+++ b/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/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c
index f3ba7b5..4aca0fb 100644
--- a/drivers/media/dvb-frontends/lnbp21.c
+++ b/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/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c
index c463da7..d7ca0fd 100644
--- a/drivers/media/dvb-frontends/lnbp22.c
+++ b/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/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index d3d928e..e9b2d2b 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/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 <crope@iki.fi>
*
@@ -18,149 +18,15 @@
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 = &reg,
- }, {
- .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)
-{
- int ret;
- u8 u8tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 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;
- }
- *val = u8tmp >> i;
-
- return 0;
-}
-
/* 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 +37,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 +48,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;
+}
+
+/*
+ * 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_dev *dev = fe->demodulator_priv;
+ unsigned tmp;
+ int ret;
+
+ 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, fe_status_t *status)
+static int m88ds3103_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
{
- 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 u8tmp;
+ 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;
- dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n",
- __func__, u8tmp, *status);
+ 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;
+ }
+
+ 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 +289,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 +336,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 +348,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 +375,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 +394,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 = regmap_update_bits(dev->regmap, 0x22, 0xc0, u8tmp1 << 6);
if (ret)
goto err;
- ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
+ ret = regmap_update_bits(dev->regmap, 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 +421,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 +430,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 = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, 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 +482,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 = regmap_update_bits(dev->regmap, 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 +523,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 +544,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 = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x01);
if (ret)
goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01);
+ ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x00);
if (ret)
goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10);
+ ret = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, utmp, 0x01, 0x00);
if (ret)
goto err;
/* sleep */
- ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
+ ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00);
if (ret)
goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+ ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01);
if (ret)
goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+ ret = regmap_update_bits(dev->regmap, 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 +821,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 +869,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 +895,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 +918,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__);
-
- 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;
- }
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
- *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 +986,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 = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, 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 +1037,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 = regmap_update_bits(dev->regmap, 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 +1079,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 = regmap_update_bits(dev->regmap, 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);
+
+ /* DiSEqC message typical period is 54 ms */
+ usleep_range(50000, 54000);
- 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 (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 = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40);
if (ret)
goto err;
}
- ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+ ret = regmap_update_bits(dev->regmap, 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 = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp);
if (ret)
goto err;
@@ -1264,43 +1164,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);
- ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
+ /* DiSEqC ToneBurst period is 12.5 ms */
+ usleep_range(8500, 12500);
+
+ 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_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+ ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40);
+ if (ret)
+ goto err;
+ }
+
+ ret = regmap_update_bits(dev->regmap, 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 +1224,290 @@ 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);
+
+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
+ },
- mutex_unlock(&priv->i2c_mutex);
+ .release = m88ds3103_release,
- return 0;
+ .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 = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00);
if (ret)
- goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+ goto err_kfree;
+ ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01);
if (ret)
- goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+ goto err_kfree;
+ ret = regmap_update_bits(dev->regmap, 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 = {
+ .owner = THIS_MODULE,
+ .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 <crope@iki.fi>");
-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/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
index 9b3b496..04b355a 100644
--- a/drivers/media/dvb-frontends/m88ds3103.h
+++ b/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 <crope@iki.fi>
*
@@ -19,6 +19,63 @@
#include <linux/dvb/frontend.h>
+/*
+ * 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/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index a2c0958..eee8c22 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/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 <crope@iki.fi>
*
@@ -22,6 +22,7 @@
#include "dvb_math.h"
#include <linux/firmware.h>
#include <linux/i2c-mux.h>
+#include <linux/regmap.h>
#include <linux/math64.h>
#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/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
index d63bc9c..9b6f464 100644
--- a/drivers/media/dvb-frontends/m88rs2000.c
+++ b/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/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c
index 3ddea44..79bc671 100644
--- a/drivers/media/dvb-frontends/mb86a16.c
+++ b/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/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h
index e486dc0..dbd5f43 100644
--- a/drivers/media/dvb-frontends/mb86a16.h
+++ b/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/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 8f54c39..cfc005e 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/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/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h
index f749c8a..a113282 100644
--- a/drivers/media/dvb-frontends/mb86a20s.h
+++ b/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/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c
index 2163490..c36e676 100644
--- a/drivers/media/dvb-frontends/mt312.c
+++ b/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/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c
index 2c3b50e..123bb2f 100644
--- a/drivers/media/dvb-frontends/mt352.c
+++ b/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/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
index 8a8e1ec..79c3040 100644
--- a/drivers/media/dvb-frontends/nxt200x.c
+++ b/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/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c
index 90ae6c7..73f9505 100644
--- a/drivers/media/dvb-frontends/nxt6000.c
+++ b/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/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c
index cbbd259..35b1053 100644
--- a/drivers/media/dvb-frontends/or51132.c
+++ b/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/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c
index 873ea1d..e82413b 100644
--- a/drivers/media/dvb-frontends/or51211.c
+++ b/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/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
index e1b8df6..3d01f4f 100644
--- a/drivers/media/dvb-frontends/rtl2830.c
+++ b/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);
diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h
index d50d537..cf793f3 100644
--- a/drivers/media/dvb-frontends/rtl2830_priv.h
+++ b/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/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index b400f7b..822ea4b 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
index a8e912e..c29a4c2 100644
--- a/drivers/media/dvb-frontends/rtl2832.h
+++ b/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/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h
index c3a922c..5dcd3a4 100644
--- a/drivers/media/dvb-frontends/rtl2832_priv.h
+++ b/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/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 3ff8806..7edb885 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -39,6 +39,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)
@@ -108,14 +112,15 @@ struct rtl2832_sdr_frame_buf {
};
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;
@@ -351,7 +356,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 +365,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 +394,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;
@@ -742,6 +747,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 +860,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 +921,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 +973,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 +1007,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 +1017,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: <unknown>", 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 +1039,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 +1061,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 +1071,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 +1092,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 +1137,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 +1155,6 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
} else {
ret = -EINVAL;
}
-
return ret;
}
@@ -1329,6 +1394,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 +1454,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");
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h
index d259476..342ea84 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.h
+++ b/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/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index 5ff474a..1096484 100644
--- a/drivers/media/dvb-frontends/s5h1409.c
+++ b/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/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index 64f35fe..9afc3f4 100644
--- a/drivers/media/dvb-frontends/s5h1411.c
+++ b/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/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c
index 93eeaf7..9c22a4c 100644
--- a/drivers/media/dvb-frontends/s5h1420.c
+++ b/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;
@@ -180,7 +182,7 @@ static int s5h1420_send_master_cmd (struct dvb_frontend* fe,
int result = 0;
dprintk("enter %s\n", __func__);
- if (cmd->msg_len > 8)
+ if (cmd->msg_len > sizeof(cmd->msg))
return -EINVAL;
/* setup for DISEQC */
@@ -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/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c
index 6ec16a2..4215652 100644
--- a/drivers/media/dvb-frontends/s5h1432.c
+++ b/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/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c
index 69862e1..b2d9fe1 100644
--- a/drivers/media/dvb-frontends/s921.c
+++ b/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;
diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h
index 7d3999a..f5b722d 100644
--- a/drivers/media/dvb-frontends/s921.h
+++ b/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/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 4cc5d10..7c2eeee 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/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/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 5db588e..25e238c 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/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);
@@ -508,6 +553,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 +608,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 +704,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 +719,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);
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
index 70d702a..3225d0c 100644
--- a/drivers/media/dvb-frontends/si2168.h
+++ b/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/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
index d7efce8..c07e6fe 100644
--- a/drivers/media/dvb-frontends/si2168_priv.h
+++ b/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/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c
index 16850e2..62ad7a7 100644
--- a/drivers/media/dvb-frontends/si21xx.c
+++ b/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/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c
index 57dc2aba..e87ac30 100644
--- a/drivers/media/dvb-frontends/sp8870.c
+++ b/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/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c
index 1bb81b5..4378fe1 100644
--- a/drivers/media/dvb-frontends/sp887x.c
+++ b/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/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index c73899d..756650f 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/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/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c
index 632b251..c93d9a4 100644
--- a/drivers/media/dvb-frontends/stv0288.c
+++ b/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/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c
index d40f226..75b4d8b 100644
--- a/drivers/media/dvb-frontends/stv0297.c
+++ b/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/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c
index b57ecf4..a817780 100644
--- a/drivers/media/dvb-frontends/stv0299.c
+++ b/drivers/media/dvb-frontends/stv0299.c
@@ -44,6 +44,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
@@ -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/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index b31ff26..ec3e18e 100644
--- a/drivers/media/dvb-frontends/stv0367.c
+++ b/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 */
@@ -2074,7 +2074,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 +2717,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/drivers/media/dvb-frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h
index 995db06..89bf6f6 100644
--- a/drivers/media/dvb-frontends/stv0367_priv.h
+++ b/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/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
index 2c88abf..fe31dd5 100644
--- a/drivers/media/dvb-frontends/stv0900_core.c
+++ b/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/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c
index a0a7b16..fa63a9e 100644
--- a/drivers/media/dvb-frontends/stv0900_sw.c
+++ b/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/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
index 0b2a934..25bdf6e 100644
--- a/drivers/media/dvb-frontends/stv090x.c
+++ b/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/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c
index b142583..91c6dcf 100644
--- a/drivers/media/dvb-frontends/stv6110.c
+++ b/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/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index dce22ce..456cdc7 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/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/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c
index 1bff7f4..a684424 100644
--- a/drivers/media/dvb-frontends/tda10021.c
+++ b/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/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c
index ca1e0d5..44a5565 100644
--- a/drivers/media/dvb-frontends/tda10023.c
+++ b/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/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c
index 71fb632..8451086 100644
--- a/drivers/media/dvb-frontends/tda10048.c
+++ b/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/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c
index a2631be..0e209b5 100644
--- a/drivers/media/dvb-frontends/tda1004x.c
+++ b/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
@@ -1005,7 +1005,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/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
index 4a19b85..f6dc630 100644
--- a/drivers/media/dvb-frontends/tda10071.c
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -203,7 +203,7 @@ error:
}
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_cmd cmd;
@@ -249,7 +249,7 @@ error:
}
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_cmd cmd;
@@ -413,7 +413,7 @@ error:
}
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_cmd cmd;
@@ -476,7 +476,7 @@ error:
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;
int ret;
@@ -668,7 +668,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
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",
@@ -1313,6 +1313,113 @@ 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_priv *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_priv *dev;
+ struct tda10071_platform_data *pdata = client->dev.platform_data;
+ int ret;
+ u8 u8tmp;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->client = client;
+ dev->i2c = client->adapter;
+ dev->cfg.demod_i2c_addr = client->addr;
+ dev->cfg.i2c_wr_max = pdata->i2c_wr_max;
+ dev->cfg.ts_mode = pdata->ts_mode;
+ dev->cfg.spec_inv = pdata->spec_inv;
+ dev->cfg.xtal = pdata->clk;
+ dev->cfg.pll_multiplier = pdata->pll_multiplier;
+ dev->cfg.tuner_i2c_addr = pdata->tuner_i2c_addr;
+
+ /* chip ID */
+ ret = tda10071_rd_reg(dev, 0xff, &u8tmp);
+ if (ret)
+ goto err_kfree;
+ if (u8tmp != 0x0f) {
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+
+ /* chip type */
+ ret = tda10071_rd_reg(dev, 0xdd, &u8tmp);
+ if (ret)
+ goto err_kfree;
+ if (u8tmp != 0x00) {
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+
+ /* chip version */
+ ret = tda10071_rd_reg(dev, 0xfe, &u8tmp);
+ if (ret)
+ goto err_kfree;
+ if (u8tmp != 0x01) {
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&dev->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.ops.release = NULL;
+ 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 = {
+ .owner = THIS_MODULE,
+ .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 <crope@iki.fi>");
MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h
index da89f42..0ffbfa5 100644
--- a/drivers/media/dvb-frontends/tda10071.h
+++ b/drivers/media/dvb-frontends/tda10071.h
@@ -24,6 +24,35 @@
#include <linux/kconfig.h>
#include <linux/dvb/frontend.h>
+/*
+ * I2C address
+ * 0x55,
+ */
+
+/**
+ * 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.
+ */
+
+struct tda10071_platform_data {
+ u32 clk;
+ u16 i2c_wr_max;
+#define TDA10071_TS_SERIAL 0
+#define TDA10071_TS_PARALLEL 1
+ u8 ts_mode;
+ bool spec_inv;
+ u8 pll_multiplier;
+ u8 tuner_i2c_addr;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+};
+
struct tda10071_config {
/* Demodulator I2C address.
* Default: none, must set
diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h
index 03f839c..54d7c71 100644
--- a/drivers/media/dvb-frontends/tda10071_priv.h
+++ b/drivers/media/dvb-frontends/tda10071_priv.h
@@ -28,20 +28,21 @@
struct tda10071_priv {
struct i2c_adapter *i2c;
struct dvb_frontend fe;
+ struct i2c_client *client;
struct tda10071_config cfg;
u8 meas_count[2];
u32 ber;
u32 ucb;
- fe_status_t fe_status;
- fe_delivery_system_t delivery_system;
+ enum fe_status fe_status;
+ enum fe_delivery_system delivery_system;
bool warm; /* FW running */
};
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/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c
index fcfe2e0..95a33e1 100644
--- a/drivers/media/dvb-frontends/tda10086.c
+++ b/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/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c
index 69e62f4..796543f 100644
--- a/drivers/media/dvb-frontends/tda8083.c
+++ b/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/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c
index 90164a3..f61b143 100644
--- a/drivers/media/dvb-frontends/ts2020.c
+++ b/drivers/media/dvb-frontends/ts2020.c
@@ -21,23 +21,32 @@
#include "dvb_frontend.h"
#include "ts2020.h"
+#include <linux/regmap.h>
+#include <linux/math64.h>
#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;
}
diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h
index 1714af9..9220e5c 100644
--- a/drivers/media/dvb-frontends/ts2020.h
+++ b/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/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c
index bb42b56..aacfdda 100644
--- a/drivers/media/dvb-frontends/ves1820.c
+++ b/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/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c
index 9c17eac..5269523 100644
--- a/drivers/media/dvb-frontends/ves1x93.c
+++ b/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/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c
index 82946cd..ef9764a 100644
--- a/drivers/media/dvb-frontends/zl10353.c
+++ b/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/drivers/media/firewire/firedtv-fe.c b/drivers/media/firewire/firedtv-fe.c
index 6fe9793..17acda6 100644
--- a/drivers/media/firewire/firedtv-fe.c
+++ b/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/drivers/media/firewire/firedtv.h b/drivers/media/firewire/firedtv.h
index 346a85b..345d1eda 100644
--- a/drivers/media/firewire/firedtv.h
+++ b/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/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 6f30ea7..71ee8f5 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -196,7 +196,8 @@ config VIDEO_ADV7183
config VIDEO_ADV7604
tristate "Analog Devices ADV7604 decoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && GPIOLIB
+ select HDMI
---help---
Support for the Analog Devices ADV7604 video decoder.
@@ -424,6 +425,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/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
index 873fe19..c70abab 100644
--- a/drivers/media/i2c/adp1653.c
+++ b/drivers/media/i2c/adp1653.c
@@ -8,6 +8,7 @@
* Contributors:
* Sakari Ailus <sakari.ailus@iki.fi>
* Tuukka Toivonen <tuukkat76@gmail.com>
+ * Pavel Machek <pavel@ucw.cz>
*
* 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 <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
#include <media/adp1653.h>
#include <media/v4l2-device.h>
@@ -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");
+ 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/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c
index 40a1a95..f0d3f5a 100644
--- a/drivers/media/i2c/adv7170.c
+++ b/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,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index d220af5..321834b 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/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,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index 28940cc..e2dd161 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/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/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 12d9320..95bcd40 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -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);
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 60ffcf0..808360f 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -29,6 +29,7 @@
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -95,6 +96,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 +132,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;
};
/*
@@ -327,6 +349,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)
@@ -1304,12 +1331,12 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs,
(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 +1531,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 +1757,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(sd, V4L2_DEVICE_NOTIFY_EVENT,
+ (void *)&adv76xx_ev_fmt);
return 0;
}
@@ -1896,7 +1928,8 @@ 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(sd, V4L2_DEVICE_NOTIFY_EVENT,
+ (void *)&adv76xx_ev_fmt);
if (handled)
*handled = true;
@@ -2102,46 +2135,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 +2222,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 +2297,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,8 +2339,9 @@ 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;
@@ -2567,6 +2631,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,17 +2672,29 @@ 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,
},
};
-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] },
{ }
};
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] },
{ }
};
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index b5a37fe..4cf79b24 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -56,6 +56,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 +86,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 +102,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 +242,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 +259,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 +383,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 +589,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)
@@ -1333,12 +1445,12 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs,
(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 +1552,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 +1592,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 +1980,155 @@ 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(sd, V4L2_DEVICE_NOTIFY_EVENT,
+ (void *)&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,8 @@ 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(sd, V4L2_DEVICE_NOTIFY_EVENT,
+ (void *)&adv7842_ev_fmt);
if (handled)
*handled = true;
}
@@ -2110,7 +2334,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 +2407,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 +2451,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 +2526,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 +2769,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 */
@@ -2809,13 +3030,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,6 +3206,7 @@ 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);
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index 69aeaf3..2984624 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/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)
+static int ak881x_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
{
- if (mf->field != V4L2_FIELD_INTERLACED ||
- mf->code != MEDIA_BUS_FMT_YUYV8_2X8)
+ if (code->pad || code->index)
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)
-{
- if (index)
- return -EINVAL;
-
- *code = MEDIA_BUS_FMT_YUYV8_2X8;
+ code->code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
@@ -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/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index bd49644..e15a789 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/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,
};
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
index d730786..af5eaf2 100644
--- a/drivers/media/i2c/ml86v7667.c
+++ b/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;
@@ -279,13 +285,15 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
.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 +304,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)
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index a10f7f8..57132cd 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/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,
};
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index edebd11..6edffc7 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -1046,16 +1046,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 +1107,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 +1131,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 +1266,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 +1279,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 +1298,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 +1308,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 +1353,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 +1363,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 +1439,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/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index b984752..2d1e25f 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/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 = {
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 08b234b..53c5ea8 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -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/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 297ef04..774e0d0 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/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/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index de803a1..d0ad6a2 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/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/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index f14c0e6..ba3c415 100644
--- a/drivers/media/i2c/saa6752hs.c
+++ b/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,
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 7147c8b..0eae5f4 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/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
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 0d0f9a9..7d51736 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/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;
@@ -1217,7 +1223,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 +1230,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,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 557f25d..636ebd6 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/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;
}
diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index ec89cfa..f68c235 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/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/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index 2e9a535..4fbdd1e 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/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/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index 441e0fd..6dfaead 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/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/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index 35d9c8d..3b6eeed 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/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/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 64f0836..de10a76 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -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/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index a246d4d..f313774 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/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/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index e3c907a..9b4f5de 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/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/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
index 93ae031..bab9ac0 100644
--- a/drivers/media/i2c/soc_camera/ov5642.c
+++ b/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/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index f4eef2f..1f8af1e 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/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/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 8daac88..f150a8b 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/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/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
index aa93d2e..8caae1c 100644
--- a/drivers/media/i2c/soc_camera/ov9640.c
+++ b/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/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
index 841dc55..03a7fc7 100644
--- a/drivers/media/i2c/soc_camera/ov9740.c
+++ b/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/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index 1752428..c769cf6 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/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/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index 9b85321..42bec9b 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -691,12 +691,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)
@@ -737,13 +742,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) {
@@ -764,6 +774,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;
}
@@ -821,13 +834,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 +894,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/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index 10c735c..b62b6dd 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -471,25 +471,31 @@ 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;
+ mf = &format->format;
+
if (!info->curr_win || !info->curr_fmt) {
ret = sr030pc30_set_params(sd);
if (ret)
@@ -523,25 +529,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;
-
- try_fmt(sd, mf);
- return 0;
-}
+ struct sr030pc30_info *info = sd ? to_sr030pc30(sd) : NULL;
+ const struct sr030pc30_format *fmt;
+ struct v4l2_mbus_framefmt *mf;
-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);
}
@@ -636,16 +645,15 @@ static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
.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/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 070c152..0c50e52 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/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:",
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 1c6bc30..24e4727 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -747,54 +747,6 @@ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl)
}
/**
- * 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
* @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl 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;
@@ -1016,10 +971,6 @@ 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,
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 68cdab9..e4fa074 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -817,24 +817,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 +1073,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 +1085,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,
};
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 787cdfb..05077cf 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -611,31 +611,6 @@ static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl)
}
/*
- * 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
* @index: index into the tvp7002_timings array
@@ -747,25 +722,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
* @enable: streaming enable or disable
@@ -924,10 +880,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/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index 00e7f04..4c72a18 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/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/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 218144a..f318ae9 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -21,6 +21,7 @@ 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
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 0baf0d29..23ce53b 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -24,6 +24,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/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c
index 2364d16..9f1f9169 100644
--- a/drivers/media/pci/bt8xx/bttv-audio-hook.c
+++ b/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/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index bc12060..3632958 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/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;
@@ -4238,6 +4239,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 +4283,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/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
index f2261df..4a90eee 100644
--- a/drivers/media/pci/bt8xx/dst.c
+++ b/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/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
index c22c4ae..c5cc14e 100644
--- a/drivers/media/pci/bt8xx/dst_ca.c
+++ b/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/drivers/media/pci/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h
index d70d98f..6a2cfdd 100644
--- a/drivers/media/pci/bt8xx/dst_common.h
+++ b/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/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig
new file mode 100644
index 0000000..3be1b2c
--- /dev/null
+++ b/drivers/media/pci/cobalt/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_COBALT
+ tristate "Cisco Cobalt support"
+ depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
+ depends on PCI_MSI && MTD_COMPLEX_MAPPINGS && GPIOLIB
+ 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/drivers/media/pci/cobalt/Makefile b/drivers/media/pci/cobalt/Makefile
new file mode 100644
index 0000000..b328955
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c
new file mode 100644
index 0000000..720e3ad
--- /dev/null
+++ b/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 <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#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/drivers/media/pci/cobalt/cobalt-alsa-pcm.c b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c
new file mode 100644
index 0000000..f0bdf10
--- /dev/null
+++ b/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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#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/drivers/media/pci/cobalt/cobalt-alsa-pcm.h b/drivers/media/pci/cobalt/cobalt-alsa-pcm.h
new file mode 100644
index 0000000..513fb1f
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-alsa.h b/drivers/media/pci/cobalt/cobalt-alsa.h
new file mode 100644
index 0000000..08db699
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-cpld.c b/drivers/media/pci/cobalt/cobalt-cpld.c
new file mode 100644
index 0000000..e83f5c9
--- /dev/null
+++ b/drivers/media/pci/cobalt/cobalt-cpld.c
@@ -0,0 +1,341 @@
+/*
+ * 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 <linux/delay.h>
+
+#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: %02x %02x %02x %02x %02x %02x\n", f_out,
+ regs[0], regs[1], regs[2], regs[3], regs[4], regs[5]);
+ 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: %02x %02x %02x %02x %02x %02x\n",
+ read_regs[0], read_regs[1], read_regs[2],
+ read_regs[3], read_regs[4], read_regs[5]);
+ }
+ if (2 - retries)
+ cobalt_info("Needed %d retries\n", 2 - retries);
+
+ return true;
+}
diff --git a/drivers/media/pci/cobalt/cobalt-cpld.h b/drivers/media/pci/cobalt/cobalt-cpld.h
new file mode 100644
index 0000000..0fc88fd
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c
new file mode 100644
index 0000000..b994b8e
--- /dev/null
+++ b/drivers/media/pci/cobalt/cobalt-driver.c
@@ -0,0 +1,832 @@
+/*
+ * 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 <linux/delay.h>
+#include <media/adv7604.h>
+#include <media/adv7842.h>
+#include <media/adv7511.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+
+#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 <hans.verkuil@cisco.com> & 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_err("PCI Express link width is not 8 lanes (%d)\n",
+ pcie_link_get_lanes(cobalt));
+ if (pcie_bus_link_get_lanes(cobalt) < 8)
+ cobalt_err("The current slot only supports %d lanes, at least 8 are needed\n",
+ pcie_bus_link_get_lanes(cobalt));
+ else
+ 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/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h
new file mode 100644
index 0000000..c206df9
--- /dev/null
+++ b/drivers/media/pci/cobalt/cobalt-driver.h
@@ -0,0 +1,380 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-dma-sg.h>
+
+#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_buffer vb;
+ struct list_head list;
+};
+
+static inline struct cobalt_buffer *to_cobalt_buffer(struct vb2_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/drivers/media/pci/cobalt/cobalt-flash.c b/drivers/media/pci/cobalt/cobalt-flash.c
new file mode 100644
index 0000000..04dcaf9
--- /dev/null
+++ b/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 <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/time.h>
+
+#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/drivers/media/pci/cobalt/cobalt-flash.h b/drivers/media/pci/cobalt/cobalt-flash.h
new file mode 100644
index 0000000..8077dae
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-i2c.c b/drivers/media/pci/cobalt/cobalt-i2c.c
new file mode 100644
index 0000000..ad16b89
--- /dev/null
+++ b/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], &regs->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, &regs->cr_sr);
+
+ /* Wait for transfer to complete (TIP = 0) */
+ start_time = jiffies;
+ status = ioread8(&regs->cr_sr);
+ while (status & M00018_SR_BITMAP_TIP_MSK) {
+ if (time_after(jiffies, start_time + adap->timeout))
+ return -ETIMEDOUT;
+ cond_resched();
+ status = ioread8(&regs->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, &regs->cr_sr);
+
+ /* Wait for transfer to complete (TIP = 0) */
+ start_time = jiffies;
+ status = ioread8(&regs->cr_sr);
+ while (status & M00018_SR_BITMAP_TIP_MSK) {
+ if (time_after(jiffies, start_time + adap->timeout))
+ return -ETIMEDOUT;
+ cond_resched();
+ status = ioread8(&regs->cr_sr);
+ }
+
+ /* Verify arbitration */
+ if (status & M00018_SR_BITMAP_AL_MSK) {
+ /* Arbitration lost! */
+ return -EIO;
+ }
+
+ /* Store data */
+ data[i] = ioread8(&regs->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, &regs->cr_sr);
+ iowrite8(0, &regs->ctr);
+ iowrite8(0, &regs->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(&regs->cr_sr);
+ } while (status & M00018_SR_BITMAP_TIP_MSK);
+
+ /* Disable I2C */
+ iowrite8(0, &regs->ctr);
+ iowrite8(0, &regs->cr_sr);
+
+ /* Calculate i2c prescaler */
+ iowrite8(prescale & 0xff, &regs->prerlo);
+ iowrite8((prescale >> 8) & 0xff, &regs->prerhi);
+ /* Enable I2C, interrupts disabled */
+ iowrite8(M00018_CTR_BITMAP_EN_MSK, &regs->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/drivers/media/pci/cobalt/cobalt-i2c.h b/drivers/media/pci/cobalt/cobalt-i2c.h
new file mode 100644
index 0000000..a4c1cfa
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/cobalt-irq.c b/drivers/media/pci/cobalt/cobalt-irq.c
new file mode 100644
index 0000000..dd4bff9
--- /dev/null
+++ b/drivers/media/pci/cobalt/cobalt-irq.c
@@ -0,0 +1,258 @@
+/*
+ * 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 <media/adv7604.h>
+
+#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.v4l2_buf.timestamp);
+ /* TODO: the sequence number should be read from the FPGA so we
+ also know about dropped frames. */
+ cb->vb.v4l2_buf.sequence = s->sequence++;
+ vb2_buffer_done(&cb->vb, (skip || s->unstable_frame) ?
+ VB2_BUF_STATE_QUEUED : 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/drivers/media/pci/cobalt/cobalt-irq.h b/drivers/media/pci/cobalt/cobalt-irq.h
new file mode 100644
index 0000000..5119484
--- /dev/null
+++ b/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 <linux/interrupt.h>
+
+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/drivers/media/pci/cobalt/cobalt-omnitek.c b/drivers/media/pci/cobalt/cobalt-omnitek.c
new file mode 100644
index 0000000..a28a848
--- /dev/null
+++ b/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 <linux/string.h>
+#include <linux/io.h>
+#include <linux/pci_regs.h>
+#include <linux/spinlock.h>
+
+#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/drivers/media/pci/cobalt/cobalt-omnitek.h b/drivers/media/pci/cobalt/cobalt-omnitek.h
new file mode 100644
index 0000000..e5c6d03
--- /dev/null
+++ b/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 <linux/scatterlist.h>
+#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/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
new file mode 100644
index 0000000..b40c2d1
--- /dev/null
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -0,0 +1,1272 @@
+/*
+ * 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 <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/math64.h>
+#include <linux/pci.h>
+#include <linux/v4l2-dv-timings.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/adv7604.h>
+#include <media/adv7842.h>
+
+#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 struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ 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->v4l2_buf.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->v4l2_buf.index];
+
+ descriptor_list_free(desc);
+}
+
+static int cobalt_buf_prepare(struct vb2_buffer *vb)
+{
+ struct cobalt_stream *s = vb->vb2_queue->drv_priv;
+
+ vb2_set_plane_payload(vb, 0, s->stride * s->height);
+ vb->v4l2_buf.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.v4l2_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_queue *q = vb->vb2_queue;
+ struct cobalt_stream *s = q->drv_priv;
+ struct cobalt_buffer *cb = to_cobalt_buffer(vb);
+ struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->v4l2_buf.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.v4l2_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.v4l2_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_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 (vb2_is_busy(&s->q))
+ return -EBUSY;
+
+ if (s->input == 1) {
+ *timings = cea1080p60;
+ return 0;
+ }
+ 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/drivers/media/pci/cobalt/cobalt-v4l2.h b/drivers/media/pci/cobalt/cobalt-v4l2.h
new file mode 100644
index 0000000..62be553
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h b/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h
new file mode 100644
index 0000000..9bc9ef1
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h b/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h
new file mode 100644
index 0000000..a480529
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h b/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h
new file mode 100644
index 0000000..602419e5
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h
new file mode 100644
index 0000000..95471c9
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h b/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h
new file mode 100644
index 0000000..384a3e1
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h b/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h
new file mode 100644
index 0000000..2a02902
--- /dev/null
+++ b/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/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h
new file mode 100644
index 0000000..bdef2df
--- /dev/null
+++ b/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/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
index 5a55630..30bbe8d 100644
--- a/drivers/media/pci/cx18/cx18-av-core.c
+++ b/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/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
index 4aeb7c6..71227a1 100644
--- a/drivers/media/pci/cx18/cx18-controls.c
+++ b/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/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index 83f5074..260e462 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/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/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 79aee30..55525af 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/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/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
index c82d25d..c986084 100644
--- a/drivers/media/pci/cx18/cx18-streams.c
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -90,6 +90,7 @@ static struct {
"encoder PCM audio",
VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
PCI_DMA_FROMDEVICE,
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
},
{ /* CX18_ENC_STREAM_TYPE_IDX */
"encoder IDX",
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c
index 0a91df2..aaf4e46 100644
--- a/drivers/media/pci/cx23885/altera-ci.c
+++ b/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/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 745caab..6e8c24c 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -572,7 +572,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 +588,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 +617,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 +857,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 +1185,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 +1794,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 +1851,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 +1908,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 +1964,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 +2010,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 +2040,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 +2086,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 +2149,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 +2168,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 +2180,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 +2240,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 +2307,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 +2416,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/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c
index 6f817d8..a6c45eb 100644
--- a/drivers/media/pci/cx23885/cx23885-f300.c
+++ b/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/drivers/media/pci/cx23885/cx23885-f300.h b/drivers/media/pci/cx23885/cx23885-f300.h
index e73344c..be14d7d 100644
--- a/drivers/media/pci/cx23885/cx23885-f300.h
+++ b/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/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index 2232b38..ec76470 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -581,7 +581,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 +602,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/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index aeda8d3..027ead4 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -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/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index 3501be9..aab7cf4 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -519,6 +519,8 @@ 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);
+ buf->vb.v4l2_buf.field = core->field;
+ buf->vb.v4l2_buf.sequence = q->count++;
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
}
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 1b2ed23..9dfa5ee 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -449,7 +449,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 +465,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 +481,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 +505,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 +897,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 +919,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/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index 9834454..34f5057 100644
--- a/drivers/media/pci/cx88/cx88-mpeg.c
+++ b/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" );
@@ -216,8 +216,6 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.v4l2_buf.index);
cx8802_start_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, list)
- buf->count = q->count++;
return 0;
}
@@ -260,7 +258,6 @@ 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__);
@@ -269,7 +266,6 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
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__);
diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
index 32eb7fd..7510e80 100644
--- a/drivers/media/pci/cx88/cx88-vbi.c
+++ b/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);
@@ -102,8 +102,6 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.v4l2_buf.index);
cx8800_start_vbi_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, list)
- buf->count = q->count++;
return 0;
}
@@ -175,7 +173,6 @@ 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);
@@ -183,7 +180,6 @@ static void buffer_queue(struct vb2_buffer *vb)
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);
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index c9decd8..400e5ca 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/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)
@@ -423,11 +422,10 @@ static int restart_video_queue(struct cx8800_dev *dev,
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.v4l2_buf.index);
start_video_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, list)
- buf->count = q->count++;
}
return 0;
}
+#endif
/* ------------------------------------------------------------------ */
@@ -523,7 +521,6 @@ 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);
@@ -531,7 +528,6 @@ static void buffer_queue(struct vb2_buffer *vb)
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);
@@ -771,6 +767,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;
}
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index b9fe1ac..785fe2e 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -327,7 +327,6 @@ struct cx88_buffer {
/* 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/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index 9e3492e..0ac2dd3 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/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/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index ed11716..88915fb 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/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/drivers/media/pci/dt3155/Kconfig b/drivers/media/pci/dt3155/Kconfig
new file mode 100644
index 0000000..5145e0d
--- /dev/null
+++ b/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/drivers/media/pci/dt3155/Makefile b/drivers/media/pci/dt3155/Makefile
new file mode 100644
index 0000000..89fa637
--- /dev/null
+++ b/drivers/media/pci/dt3155/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_DT3155) += dt3155.o
diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c
new file mode 100644
index 0000000..89d0dc7
--- /dev/null
+++ b/drivers/media/pci/dt3155/dt3155.c
@@ -0,0 +1,632 @@
+/***************************************************************************
+ * 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 <linux/module.h>
+#include <linux/version.h>
+#include <linux/stringify.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf2-dma-contig.h>
+
+#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 struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+
+{
+ 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;
+ 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_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 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 = vb;
+ 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->v4l2_buf.timestamp);
+ ipd->curr_buf->v4l2_buf.sequence = ipd->sequence++;
+ ipd->curr_buf->v4l2_buf.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE);
+
+ ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
+ list_del(&ivb->done_entry);
+ ipd->curr_buf = 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 <mitov@issp.bas.bg>");
+MODULE_VERSION(DT3155_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.h b/drivers/media/pci/dt3155/dt3155.h
index 96f01a0..4e1f4d5 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.h
+++ b/drivers/media/pci/dt3155/dt3155.h
@@ -12,24 +12,20 @@
* 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., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/* DT3155 header file */
#ifndef _DT3155_H_
#define _DT3155_H_
-#ifdef __KERNEL__
-
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
#define DT3155_NAME "dt3155"
-#define DT3155_VER_MAJ 1
-#define DT3155_VER_MIN 1
+#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) "." \
@@ -78,7 +74,10 @@
#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
@@ -153,60 +152,45 @@
/* DT3155 identificator */
#define DT3155_ID 0x20
-#ifdef CONFIG_DT3155_CCIR
-#define DMA_STRIDE 768
-#else
-#define DMA_STRIDE 640
-#endif
-
-/**
- * struct dt3155_stats - statistics structure
- *
- * @free_bufs_empty: no free image buffers
- * @corrupted_fields: corrupted fields
- * @dma_map_failed: dma mapping failed
- * @start_before_end: new started before old ended
- */
-struct dt3155_stats {
- int free_bufs_empty;
- int corrupted_fields;
- int dma_map_failed;
- int start_before_end;
-};
-
/* 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
- * @q pointer to vb2_queue 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
- * @field_count fields counter
+ * @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
- * @users open count
* @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 *q;
+ struct vb2_queue vidq;
+ struct vb2_alloc_ctx *alloc_ctx;
struct vb2_buffer *curr_buf;
struct mutex mux;
struct list_head dmaq;
spinlock_t lock;
- unsigned int field_count;
- struct dt3155_stats stats;
+ v4l2_std_id std;
+ unsigned width, height;
+ unsigned input;
+ unsigned int sequence;
void __iomem *regs;
- int users;
u8 csr2, config;
};
-#endif /* __KERNEL__ */
-
#endif /* _DT3155_H_ */
diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
index ccf548c..8a55ccb 100644
--- a/drivers/media/pci/ivtv/ivtv-controls.c
+++ b/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/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index c2e60b4..8616fa8 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -805,11 +805,11 @@ static void ivtv_init_struct2(struct ivtv *itv)
{
int i;
- for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS - 1; i++)
if (itv->card->video_inputs[i].video_type == 0)
break;
itv->nof_inputs = i;
- for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS - 1; i++)
if (itv->card->audio_inputs[i].audio_type == 0)
break;
itv->nof_audio_inputs = i;
diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h
index e8b6c7a..ee0ef6e 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.h
+++ b/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/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 6fe6c4a..9a21c17 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/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/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c
index 104914a..68b5800 100644
--- a/drivers/media/pci/mantis/hopper_cards.c
+++ b/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/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c
index 801fc55..cdefffc 100644
--- a/drivers/media/pci/mantis/mantis_cards.c
+++ b/drivers/media/pci/mantis/mantis_cards.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
+#include <media/rc-map.h>
#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/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h
index 8ff448b..d48778a 100644
--- a/drivers/media/pci/mantis/mantis_common.h
+++ b/drivers/media/pci/mantis/mantis_common.h
@@ -25,6 +25,7 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#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/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c
index 566c407..1d59c7e 100644
--- a/drivers/media/pci/mantis/mantis_dma.c
+++ b/drivers/media/pci/mantis/mantis_dma.c
@@ -190,7 +190,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 +209,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/drivers/media/pci/mantis/mantis_i2c.c b/drivers/media/pci/mantis/mantis_i2c.c
index 895ddba..d72ee47 100644
--- a/drivers/media/pci/mantis/mantis_i2c.c
+++ b/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/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
index 0e5252e..7f7f1d4 100644
--- a/drivers/media/pci/mantis/mantis_input.c
+++ b/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 <media/rc-core.h>
#include <linux/pci.h>
@@ -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/drivers/media/pci/mantis/mantis_input.h b/drivers/media/pci/mantis/mantis_input.h
new file mode 100644
index 0000000..0fbd929
--- /dev/null
+++ b/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/drivers/media/pci/mantis/mantis_pcmcia.c b/drivers/media/pci/mantis/mantis_pcmcia.c
index 2f188c0..b2dbc7b 100644
--- a/drivers/media/pci/mantis/mantis_pcmcia.c
+++ b/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/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c
index a707192..f1c96ae 100644
--- a/drivers/media/pci/mantis/mantis_uart.c
+++ b/drivers/media/pci/mantis/mantis_uart.c
@@ -25,6 +25,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
+#include <linux/pci.h>
#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/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c
index 7c1bd16..3b19285 100644
--- a/drivers/media/pci/mantis/mantis_vp1034.c
+++ b/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/drivers/media/pci/mantis/mantis_vp1034.h b/drivers/media/pci/mantis/mantis_vp1034.h
index 323f38e..764b1c6 100644
--- a/drivers/media/pci/mantis/mantis_vp1034.h
+++ b/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/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index e29bc3af4..1b92d83 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/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/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h
index 51e2fbd..fa30930 100644
--- a/drivers/media/pci/ngene/ngene.h
+++ b/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/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index acc35b4..e7e4428 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/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/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c
index 1b637b7..d0e70dc0 100644
--- a/drivers/media/pci/pt1/va1j5jf8007s.c
+++ b/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/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c
index 2db15159..0268f20 100644
--- a/drivers/media/pci/pt1/va1j5jf8007t.c
+++ b/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/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c
index 7a37e8f..0d2e2b2 100644
--- a/drivers/media/pci/pt3/pt3.c
+++ b/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/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index ac3cd74..1d2c310 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -16,6 +16,9 @@
*
*/
+#include "saa7134.h"
+#include "saa7134-reg.h"
+
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
@@ -29,13 +32,6 @@
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
-#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;
@@ -1240,19 +1233,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;
@@ -1272,7 +1265,7 @@ static void saa7134_alsa_exit(void)
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/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
index 3ca0780..c740576 100644
--- a/drivers/media/pci/saa7134/saa7134-cards.c
+++ b/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 <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
#include "tuner-xc2028.h"
#include <media/v4l2-common.h>
#include <media/tveeprom.h>
@@ -5850,6 +5851,39 @@ 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,
+ },
+ },
};
@@ -7109,6 +7143,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.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,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -7158,10 +7198,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=<nr> 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=<nr> 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 +7234,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 +7420,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 +7451,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 +7467,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 +7488,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:
@@ -7512,10 +7553,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 +7681,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 +7830,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 +7838,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 +7852,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 +7867,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 +7885,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 +7914,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 +7942,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 +7958,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 +8028,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 +8074,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 +8099,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 +8116,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/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index a349e96..72d7f99 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -33,9 +36,6 @@
#include <linux/dma-mapping.h>
#include <linux/pm.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
-
MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [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;
}
@@ -274,7 +281,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,7 +305,7 @@ 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);
@@ -318,18 +325,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 +358,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);
@@ -474,7 +481,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 +503,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 +539,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 +617,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 +683,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 +745,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 +759,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 +775,32 @@ static void must_configure_manually(int has_eeprom)
unsigned int i,p;
if (!has_eeprom)
- printk(KERN_WARNING
- "saa7134: <rant>\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: </rant>\n"
- "saa7134: I feel better now. Ok, here are the good news:\n"
- "saa7134: You can use the card=<nr> insmod option to specify\n"
- "saa7134: which board do you have. The list:\n");
+ pr_warn("saa7134: <rant>\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: </rant>\n"
+ "saa7134: I feel better now. Ok, here are the good news:\n"
+ "saa7134: You can use the card=<nr> 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=<nr>\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=<nr>\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 +904,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 +936,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,13 +944,13 @@ 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);
+ pr_warn("%s: Oops: no 32bit PCI DMA ???\n", dev->name);
err = -EIO;
goto fail1;
}
@@ -972,7 +973,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 +984,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 +993,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 +1011,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 +1041,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 +1060,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 +1069,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 +1085,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 +1096,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 +1205,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 +1359,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/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c
index 73ffbab..101ba87 100644
--- a/drivers/media/pci/saa7134/saa7134-dvb.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -28,8 +31,6 @@
#include <linux/kthread.h>
#include <linux/suspend.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
#include <media/v4l2-common.h>
#include "dvb-pll.h"
#include <dvb_frontend.h>
@@ -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/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index 594dc3a..56b932c 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -26,9 +29,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
-
/* ------------------------------------------------------------------ */
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [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/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c
index 54e650b..8a2abb3 100644
--- a/drivers/media/pci/saa7134/saa7134-go7007.c
+++ b/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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -27,8 +30,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#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/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c
index f4da674..8ef6399 100644
--- a/drivers/media/pci/saa7134/saa7134-i2c.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
#include <media/v4l2-common.h>
/* ----------------------------------------------------------- */
@@ -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/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index dc3d651..11a1720 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -18,15 +18,15 @@
*
*/
+#include "saa7134.h"
+#include "saa7134-reg.h"
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-#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
/* <dev> 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,
/* <dev> 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:
@@ -831,8 +837,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
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 +921,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 +964,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 +979,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 +1024,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/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c
index 2709b83..4b202fa 100644
--- a/drivers/media/pci/saa7134/saa7134-ts.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#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;
@@ -95,7 +97,7 @@ int saa7134_ts_buffer_prepare(struct vb2_buffer *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;
@@ -239,7 +241,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 +263,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/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c
index 3afbcb7..21a5793 100644
--- a/drivers/media/pci/saa7134/saa7134-tvaudio.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -29,9 +32,6 @@
#include <linux/freezer.h>
#include <asm/div64.h>
-#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/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
index 5306e54..4d36586 100644
--- a/drivers/media/pci/saa7134/saa7134-vbi.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#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)
/* ------------------------------------------------------------------ */
@@ -84,7 +86,7 @@ static int buffer_activate(struct saa7134_dev *dev,
struct saa7134_dmaqueue *dmaq = buf->vb2.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);
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 99d09a7..035039c 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/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 <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -31,9 +34,6 @@
#include <media/v4l2-event.h>
#include <media/saa6588.h>
-#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]);
@@ -792,7 +795,7 @@ static int buffer_activate(struct saa7134_dev *dev,
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 */
@@ -1229,7 +1232,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 +1308,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 +1342,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 +1741,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 +1798,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 +2187,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/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index 8bf0553..6b5f6f4 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -21,6 +21,8 @@
#define SAA7134_VERSION "0, 2, 17"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
@@ -339,6 +341,7 @@ 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_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
@@ -654,7 +657,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);
diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c
index 4f3b1dd..e7e586c 100644
--- a/drivers/media/pci/saa7164/saa7164-api.c
+++ b/drivers/media/pci/saa7164/saa7164-api.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
index 9bd1f73..f30758e 100644
--- a/drivers/media/pci/saa7164/saa7164-buffer.c
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index 6c73f5b..a18fe5d 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c
index 5b72da5..c2b7382 100644
--- a/drivers/media/pci/saa7164/saa7164-cards.c
+++ b/drivers/media/pci/saa7164/saa7164-cards.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index cfabcba..3285c37 100644
--- a/drivers/media/pci/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index 9cf3c6c..3206a82 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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)
{
@@ -1230,13 +1270,22 @@ static int saa7164_initdev(struct pci_dev *pci_dev,
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/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
index 16ae715..e9a783b 100644
--- a/drivers/media/pci/saa7164/saa7164-dvb.c
+++ b/drivers/media/pci/saa7164/saa7164-dvb.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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__);
@@ -528,6 +615,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",
dev->name);
@@ -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/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c
index 9266965..4434e0f 100644
--- a/drivers/media/pci/saa7164/saa7164-encoder.c
+++ b/drivers/media/pci/saa7164/saa7164-encoder.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -721,13 +721,14 @@ static int vidioc_querycap(struct file *file, void *priv,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities =
+ cap->device_caps =
V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- 0;
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_TUNER;
- cap->capabilities |= V4L2_CAP_TUNER;
- cap->version = 0;
+ cap->capabilities = cap->device_caps |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index add06ab..269e078 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-i2c.c b/drivers/media/pci/saa7164/saa7164-i2c.c
index 4f7e3b4..0342d84 100644
--- a/drivers/media/pci/saa7164/saa7164-i2c.c
+++ b/drivers/media/pci/saa7164/saa7164-i2c.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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/drivers/media/pci/saa7164/saa7164-reg.h b/drivers/media/pci/saa7164/saa7164-reg.h
index 2bbf815..37521a2 100644
--- a/drivers/media/pci/saa7164/saa7164-reg.h
+++ b/drivers/media/pci/saa7164/saa7164-reg.h
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-types.h b/drivers/media/pci/saa7164/saa7164-types.h
index f48ba97..1efba6c 100644
--- a/drivers/media/pci/saa7164/saa7164-types.h
+++ b/drivers/media/pci/saa7164/saa7164-types.h
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c
index 6e025fe..859fd03 100644
--- a/drivers/media/pci/saa7164/saa7164-vbi.c
+++ b/drivers/media/pci/saa7164/saa7164-vbi.c
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -660,13 +660,14 @@ static int vidioc_querycap(struct file *file, void *priv,
sizeof(cap->card));
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities =
+ cap->device_caps =
V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- 0;
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_TUNER;
- cap->capabilities |= V4L2_CAP_TUNER;
- cap->version = 0;
+ cap->capabilities = cap->device_caps |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index cd1a07c..18906e0 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -1,7 +1,7 @@
/*
* Driver for the NXP SAA7164 PCIe bridge
*
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.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
@@ -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
@@ -371,6 +374,8 @@ 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 */
@@ -459,6 +464,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;
diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c
index 4115925..143fd78 100644
--- a/drivers/media/pci/smipcie/smipcie.c
+++ b/drivers/media/pci/smipcie/smipcie.c
@@ -657,6 +657,7 @@ static int smi_dvbsky_sit2_fe_attach(struct smi_port *port)
/* 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);
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index d384a6b..59b3a36 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -813,7 +813,7 @@ 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);
+ v4l2_get_timestamp(&vip->active->vb.v4l2_buf.timestamp);
vip->active->vb.v4l2_buf.sequence = vip->sequence++;
vb2_buffer_done(&vip->active->vb, VB2_BUF_STATE_DONE);
}
@@ -864,6 +864,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/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index 45199a1..3f24fce 100644
--- a/drivers/media/pci/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -1172,7 +1172,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 +1197,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 +1946,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 +2008,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 +2044,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 +2057,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 +2070,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/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
index 835635b..3a55927 100644
--- a/drivers/media/pci/ttpci/av7110.h
+++ b/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/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
index 23e0549..e9674b4 100644
--- a/drivers/media/pci/ttpci/budget-core.c
+++ b/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/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c
index a4d8867..b5b6596 100644
--- a/drivers/media/pci/ttpci/budget-patch.c
+++ b/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/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
index 6ccc488..99972be 100644
--- a/drivers/media/pci/ttpci/budget.c
+++ b/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/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
index 3d8a806..1ccbe1a 100644
--- a/drivers/media/pci/ttpci/budget.h
+++ b/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/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c
index b6801e0..40119b3 100644
--- a/drivers/media/pci/zoran/zoran_device.c
+++ b/drivers/media/pci/zoran/zoran_device.c
@@ -1584,14 +1584,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/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 421f531..a4e7d21 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -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
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 8f85561..114f9ab 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -34,6 +34,8 @@ 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_BLACKFIN) += blackfin/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index a30cc2f..1fba339 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/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)
@@ -430,7 +431,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 +511,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 +1096,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
@@ -1432,8 +1433,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);
@@ -1455,7 +1456,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;
@@ -1472,23 +1472,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;
@@ -1675,12 +1663,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;
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 6a437f8..b7e70fb 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -156,14 +156,18 @@ static struct bcap_buffer *to_bcap_vb(struct vb2_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 */
@@ -597,7 +602,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 +616,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 +674,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 +689,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/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index d043007..109797b 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -1305,7 +1305,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)
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)
@@ -1975,7 +1975,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
}
vb2_set_plane_payload(dst_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,
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 8e6fe02..6d6e0ca 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -724,35 +724,30 @@ 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_buffer *buf)
{
struct vb2_queue *src_vq;
src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
- (buf->sequence == (ctx->qsequence - 1)));
+ (buf->v4l2_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_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->v4l2_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,
@@ -865,7 +860,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,
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 6a5c8f6..8e0af22 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -287,6 +287,9 @@ 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_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);
diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h
index d1d06cb..781bf72 100644
--- a/drivers/media/platform/coda/trace.h
+++ b/drivers/media/platform/coda/trace.h
@@ -9,8 +9,6 @@
#include "coda.h"
-#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
-
TRACE_EVENT(coda_bit_run,
TP_PROTO(struct coda_ctx *ctx, int cmd),
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index c4ab46f..f69cdd7 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -71,15 +71,10 @@ 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;
+
+ v4l2_get_timestamp(&layer->cur_frm->vb.v4l2_buf.timestamp);
vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
layer->cur_frm = layer->next_frm;
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index ccfcf3f..7767e07 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/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/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index fd2891c..9b9e423 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/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/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index b7b2e47..40423c6 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/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/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index f315ef9..4f5586a 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/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/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index bbf4281..5b76e3d 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -1664,7 +1664,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/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 92d9549..c07f367 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -1060,7 +1060,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/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index 5628453..77890bd 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/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/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 110fd70..5e2b4df 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -24,6 +24,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/ov7670.h>
#include <media/videobuf2-vmalloc.h>
#include <media/videobuf2-dma-contig.h>
@@ -123,29 +124,22 @@ static struct mcam_format_struct {
.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,12 +199,6 @@ 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
@@ -221,7 +210,6 @@ struct mcam_vb_buffer {
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)
@@ -237,6 +225,8 @@ static void mcam_buffer_done(struct mcam_camera *cam, int frame,
{
vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage;
vbuf->v4l2_buf.sequence = cam->buf_seq[frame];
+ vbuf->v4l2_buf.field = V4L2_FIELD_NONE;
+ v4l2_get_timestamp(&vbuf->v4l2_buf.timestamp);
vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage);
vb2_buffer_done(vbuf, 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);
@@ -510,14 +536,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,9 +547,7 @@ 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;
/*
@@ -555,32 +571,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
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);
- }
+ mcam_write_yuv_bases(cam, frame, dma_handle);
}
/*
@@ -603,6 +594,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 +744,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 +752,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 +764,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 +777,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 +951,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 +958,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 +978,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...
*/
@@ -1073,7 +1055,9 @@ static int mcam_vb_queue_setup(struct vb2_queue *vq,
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;
@@ -1102,6 +1086,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, 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, 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 +1117,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 +1146,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 +1177,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);
}
@@ -1246,14 +1261,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 +1281,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 +1294,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 +1304,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 +1320,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 +1349,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 +1388,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 +1403,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 +1416,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 +1427,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 +1443,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 +1460,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 +1467,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 +1478,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 +1489,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 +1501,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 +1524,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 +1538,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 +1550,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 +1562,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 +1580,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 +1607,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 +1647,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 +1664,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 +1681,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,10 +1794,17 @@ 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.
*/
sensor_cfg.clock_speed = cam->clock_speed;
@@ -1943,21 +1820,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 +1846,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 +1864,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 +1884,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 +1899,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/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 7ffdf4d..97167f6 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -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/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 0ed9b3a..b5f165a 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/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/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 17b189a..f09c5f1 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -445,7 +445,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 +468,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 +479,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;
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 15cb254..1380327 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/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/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index f6a61b9..76e6289 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -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;
@@ -449,19 +449,22 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
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)
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index 2d5bd3a..f47b332 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -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/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index ec3e124..421a7c3 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -787,7 +787,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/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 8333fbc2..8de61dc 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -211,6 +211,7 @@ static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
else
dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ dst_buf->b->v4l2_buf.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);
@@ -1337,8 +1338,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 +1462,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/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index b09bcd1..9a923b1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -184,7 +184,7 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &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));
@@ -263,7 +263,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 +271,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)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index cefad18..12497f5 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -1852,7 +1852,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 +1862,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/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 0e74aab..7994075 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/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/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c
index 2a9501d..5ef6777 100644
--- a/drivers/media/platform/s5p-tv/mixer_drv.c
+++ b/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/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
index 3621af9..c75d435 100644
--- a/drivers/media/platform/s5p-tv/sdo_drv.c
+++ b/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/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index dde1ccc..8b799ba 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -600,7 +600,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 +625,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 +633,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 +655,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,12 +663,12 @@ 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,
@@ -679,12 +679,14 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
unsigned int img_height_max;
int pix_idx;
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,
};
+ struct v4l2_mbus_framefmt *mbfmt = &format.format;
int ret;
dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
@@ -720,27 +722,27 @@ 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);
/* 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);
}
@@ -942,11 +944,12 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
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;
@@ -984,22 +987,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:
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index c835beb..2879026 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -487,7 +487,10 @@ static int isi_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);
@@ -500,27 +503,27 @@ 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,7 +538,11 @@ 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;
@@ -552,21 +559,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 +581,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 +655,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 +682,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 +690,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 +709,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++;
}
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 192377f..ea4c423 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -912,7 +912,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 +926,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 +946,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 +973,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 +996,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 +1127,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 +1143,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 +1163,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 +1190,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 +1217,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 +1234,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);
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 3435fd2..ace41f5 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -659,18 +659,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 +682,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 +712,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 +804,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 +818,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 +854,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 +878,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 +897,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 +915,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 +936,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 +958,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/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 16f65ec..ba8dcd1 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/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/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 8d6e343..fcb942d 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/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/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 6460f8e..db7700b 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -899,7 +899,7 @@ static irqreturn_t rcar_vin_irq(int irq, void *data)
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);
+ v4l2_get_timestamp(&priv->queue_buf[slot]->v4l2_buf.timestamp);
vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE);
priv->queue_buf[slot] = NULL;
@@ -1323,16 +1323,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 +1344,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 +1362,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 +1373,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 +1390,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 +1405,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,7 +1419,7 @@ 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:
@@ -1427,9 +1433,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 +1451,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 +1476,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 +1503,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;
@@ -1679,7 +1688,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 +1719,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 +1748,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 +1761,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;
}
@@ -1808,7 +1821,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 },
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 9ce202f..c5c6c4e 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -1048,17 +1048,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 +1073,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 +1094,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 +1105,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 +1120,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 +1134,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 +1146,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 +1169,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 +1184,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 +1220,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 +1253,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 +1273,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 +1281,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 +1499,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 +1531,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 +1560,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 +1573,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);
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index cd93241..12d3626 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/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/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 7bfe7665..d708df4 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -484,10 +484,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 +525,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]);
@@ -1284,7 +1289,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 +1322,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);
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index f535910..cc8eb07 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/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/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
index 8e74fb7..bda29bc 100644
--- a/drivers/media/platform/soc_camera/soc_scale_crop.c
+++ b/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/drivers/media/platform/sti/bdisp/Makefile b/drivers/media/platform/sti/bdisp/Makefile
new file mode 100644
index 0000000..bc53496
--- /dev/null
+++ b/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/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
new file mode 100644
index 0000000..18282a0
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+
+#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_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_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_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/drivers/media/platform/sti/bdisp/bdisp-filter.h b/drivers/media/platform/sti/bdisp/bdisp-filter.h
new file mode 100644
index 0000000..fc8c54f
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp-filter.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> 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/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c
new file mode 100644
index 0000000..465828e
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/delay.h>
+
+#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.width;
+ dst_h = ctx->dst.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),
+ 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/drivers/media/platform/sti/bdisp/bdisp-reg.h b/drivers/media/platform/sti/bdisp/bdisp-reg.h
new file mode 100644
index 0000000..e7e1a42
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp-reg.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> 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/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
new file mode 100644
index 0000000..9e782eb
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#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_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->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 &
+ 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_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, 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, dst, dst->paddr);
+ if (ret)
+ return ret;
+
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.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 struct v4l2_format *fmt,
+ unsigned int *nb_buf, unsigned int *nb_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ 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 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, vb);
+}
+
+static int bdisp_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct bdisp_ctx *ctx = q->drv_priv;
+ struct vb2_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);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /* Composing / capture is not supported */
+ dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\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);
+ }
+
+ 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_dbg(ctx->bdisp_dev->dev, "Invalid target\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;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /* Composing / capture is not supported */
+ dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n");
+ return -EINVAL;
+ }
+
+ if (s->target != V4L2_SEL_TGT_CROP) {
+ dev_dbg(ctx->bdisp_dev->dev, "Invalid 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 <fabien.dessenne@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/sti/bdisp/bdisp.h
new file mode 100644
index 0000000..0cf9857
--- /dev/null
+++ b/drivers/media/platform/sti/bdisp/bdisp.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/ktime.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#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/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index 678ed9f..32e4ff4 100644
--- a/drivers/media/platform/via-camera.c
+++ b/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/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index 4d6b4cc..295fde5 100644
--- a/drivers/media/platform/vim2m.c
+++ b/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 */
@@ -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,
@@ -724,6 +722,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)--;
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index d33f164..a047b47 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -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);
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 9e15aee..c72349c 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -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;
@@ -140,7 +139,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 +332,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,6 +447,8 @@ 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;
int sdr_fixp_src_phase;
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index 2b90700..339c8b7 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/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)
@@ -360,6 +361,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);
@@ -709,6 +717,25 @@ static const struct v4l2_ctrl_config vivid_ctrl_colorspace = {
.qmenu = vivid_ctrl_colorspace_strings,
};
+static const char * const vivid_ctrl_xfer_func_strings[] = {
+ "Default",
+ "Rec. 709",
+ "sRGB",
+ "AdobeRGB",
+ "SMPTE 240M",
+ "None",
+ 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 = 5,
+ .qmenu = vivid_ctrl_xfer_func_strings,
+};
+
static const char * const vivid_ctrl_ycbcr_enc_strings[] = {
"Default",
"ITU-R 601",
@@ -766,6 +793,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)
@@ -1199,38 +1257,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_radio_tx_rds_blockio = {
};
-
-/* Video Loop Control */
-
-static int vivid_loop_out_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_out);
-
- 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_out_ctrl_ops = {
- .s_ctrl = vivid_loop_out_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_class = {
.ops = &vivid_user_gen_ctrl_ops,
.flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY,
@@ -1248,7 +1274,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 +1300,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);
@@ -1365,6 +1391,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 +1472,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);
@@ -1528,8 +1555,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 +1567,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 +1576,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 +1584,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 +1592,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 +1634,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/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c
index c7651a5..f99092c 100644
--- a/drivers/media/platform/vivid/vivid-radio-rx.c
+++ b/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/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index caf1316..d2f2188 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -33,6 +33,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 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,
@@ -409,21 +428,63 @@ 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))
@@ -477,11 +538,24 @@ 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 */
+ /* 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 */
+ fixp_i = fixp_i * 1275;
+ fixp_q = fixp_q * 1275;
+ *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10);
+ *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10);
+ break;
+ default:
+ break;
+ }
}
}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h
index 79c1890..43014b2 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.h
+++ b/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/drivers/media/platform/vivid/vivid-tpg-colors.c b/drivers/media/platform/vivid/vivid-tpg-colors.c
index 424aa7a..8f231a6 100644
--- a/drivers/media/platform/vivid/vivid-tpg-colors.c
+++ b/drivers/media/platform/vivid/vivid-tpg-colors.c
@@ -598,71 +598,327 @@ 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_BT2020 + 1][V4L2_XFER_FUNC_NONE + 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_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_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_470_SYSTEM_M][V4L2_XFER_FUNC_709][0] = { 2892, 2988, 2807 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][1] = { 2846, 3070, 843 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][2] = { 1656, 2962, 2783 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][3] = { 1572, 3045, 763 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][4] = { 2476, 229, 2742 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][5] = { 2420, 672, 614 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][6] = { 725, 63, 2718 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][7] = { 534, 561, 509 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][0] = { 3013, 3099, 2935 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][1] = { 2970, 3174, 1091 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][2] = { 1871, 3076, 2913 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][3] = { 1791, 3152, 1013 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][4] = { 2632, 468, 2876 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][5] = { 2581, 924, 866 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][6] = { 976, 180, 2854 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][7] = { 786, 813, 762 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][0] = { 2990, 3077, 2912 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][1] = { 2947, 3153, 1119 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][2] = { 1859, 3053, 2889 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][3] = { 1782, 3130, 1047 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][4] = { 2608, 556, 2852 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][5] = { 2557, 964, 912 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][6] = { 1013, 309, 2830 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_ADOBERGB][7] = { 839, 864, 817 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2879, 2975, 2793 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2832, 3059, 806 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][2] = { 1629, 2949, 2768 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][3] = { 1543, 3033, 725 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][4] = { 2457, 203, 2727 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][5] = { 2401, 633, 574 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][6] = { 687, 56, 2702 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][7] = { 493, 521, 469 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][0] = { 2060, 2194, 1943 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][1] = { 1995, 2314, 237 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][2] = { 725, 2157, 1911 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][3] = { 660, 2278, 205 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][4] = { 1525, 50, 1857 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][5] = { 1461, 171, 151 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][6] = { 190, 14, 1825 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][7] = { 126, 134, 118 },
+ [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_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_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_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 },
};
#else
@@ -764,50 +1020,38 @@ 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_SRGB:
case V4L2_COLORSPACE_REC709:
+ break;
default:
break;
}
@@ -818,33 +1062,28 @@ 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_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;
}
}
@@ -877,7 +1116,16 @@ int main(int argc, char **argv)
"V4L2_COLORSPACE_ADOBERGB",
"V4L2_COLORSPACE_BT2020",
};
+ 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",
+ };
int i;
+ int x;
int c;
printf("/* Generated table */\n");
@@ -905,22 +1153,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");
+ printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][V4L2_XFER_FUNC_NONE + 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;
+ for (x = 1; x <= V4L2_XFER_FUNC_NONE; 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/drivers/media/platform/vivid/vivid-tpg-colors.h b/drivers/media/platform/vivid/vivid-tpg-colors.h
index 2c33335..86b8bf3 100644
--- a/drivers/media/platform/vivid/vivid-tpg-colors.h
+++ b/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_BT2020 + 1]
+ [V4L2_XFER_FUNC_NONE + 1]
+ [TPG_COLOR_CSC_BLACK + 1];
#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
index cb766eb..1458c79 100644
--- a/drivers/media/platform/vivid/vivid-tpg.c
+++ b/drivers/media/platform/vivid/vivid-tpg.c
@@ -220,6 +220,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 +294,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 +316,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 +334,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:
@@ -479,44 +481,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 +596,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 +668,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 +706,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 +899,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:
@@ -1395,42 +1469,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 +1523,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 +1692,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 +1752,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/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
index a50cd2e..9baed6a 100644
--- a/drivers/media/platform/vivid/vivid-tpg.h
+++ b/drivers/media/platform/vivid/vivid-tpg.h
@@ -122,8 +122,14 @@ 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/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index dab5990..c4268d1 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/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 },
@@ -501,6 +501,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 +534,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 +624,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 +729,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 +1039,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;
}
@@ -1620,7 +1628,7 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
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))
+ bt->polarities, bt->interlaced, timings))
return true;
}
@@ -1631,7 +1639,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 +1777,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 +1807,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/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index aa44627..fc73927 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/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,6 @@ struct vivid_fmt vivid_formats[] = {
.buffers = 1,
},
{
- .name = "4:2:2, biplanar, YUV",
.fourcc = V4L2_PIX_FMT_NV16M,
.vdownsampling = { 1, 1 },
.bit_depth = { 8, 8 },
@@ -427,7 +399,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 +408,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 +416,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 +424,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 +432,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 +524,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 +553,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 +719,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/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 0af43dc..0862c1f 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -258,6 +258,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 +321,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 +409,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 +549,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) {
@@ -1152,7 +1156,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/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index d7324c7..84bae79 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/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/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index efde88a..98e50e4 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -653,7 +653,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;
@@ -725,7 +725,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/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index dccf586..9cbb8cd 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/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/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index e6b55ed..04baafe 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/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/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 2a497c8..471d6a8 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/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/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 57f0bc3..091d793 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/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/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 4b76604..6c0ca90 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/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/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index e9d03ac..0b04b56 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/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/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
index 9d6574b..c9e349b 100644
--- a/drivers/media/radio/wl128x/Kconfig
+++ b/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/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index a587c9b..dd203de 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/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/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 5c63c2e..bd7b3bd 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -33,7 +33,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <media/rc-core.h>
-#include <linux/pci_ids.h>
#include "fintek-cir.h"
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 229853d..7dbc9ca 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/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/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index 58ec598..1c087cb 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/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) {
@@ -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/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 84fa6e9..8939ebd 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -184,9 +184,125 @@ out:
return -EINVAL;
}
+static struct ir_raw_timings_manchester ir_rc5_timings = {
+ .leader = RC5_UNIT,
+ .pulse_space_start = 0,
+ .clock = RC5_UNIT,
+ .trailer_space = RC5_UNIT * 10,
+};
+
+static struct ir_raw_timings_manchester ir_rc5x_timings[2] = {
+ {
+ .leader = RC5_UNIT,
+ .pulse_space_start = 0,
+ .clock = RC5_UNIT,
+ .trailer_space = RC5X_SPACE,
+ },
+ {
+ .clock = RC5_UNIT,
+ .trailer_space = RC5_UNIT * 10,
+ },
+};
+
+static struct ir_raw_timings_manchester ir_rc5_sz_timings = {
+ .leader = RC5_UNIT,
+ .pulse_space_start = 0,
+ .clock = RC5_UNIT,
+ .trailer_space = RC5_UNIT * 10,
+};
+
+static int ir_rc5_validate_filter(const struct rc_scancode_filter *scancode,
+ unsigned int important_bits)
+{
+ /* all important bits of scancode should be set in mask */
+ if (~scancode->mask & important_bits)
+ return -EINVAL;
+ /* extra bits in mask should be zero in data */
+ if (scancode->mask & scancode->data & ~important_bits)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * ir_rc5_encode() - Encode a scancode as a stream of raw events
+ *
+ * @protocols: allowed protocols
+ * @scancode: scancode filter describing scancode (helps distinguish between
+ * protocol subtypes when scancode is ambiguous)
+ * @events: array of raw ir events to write into
+ * @max: maximum size of @events
+ *
+ * Returns: The number of events written.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * encoding. In this case all @max events will have been written.
+ * -EINVAL if the scancode is ambiguous or invalid.
+ */
+static int ir_rc5_encode(u64 protocols,
+ const struct rc_scancode_filter *scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ int ret;
+ struct ir_raw_event *e = events;
+ unsigned int data, xdata, command, commandx, system;
+
+ /* Detect protocol and convert scancode to raw data */
+ if (protocols & RC_BIT_RC5 &&
+ !ir_rc5_validate_filter(scancode, 0x1f7f)) {
+ /* decode scancode */
+ command = (scancode->data & 0x003f) >> 0;
+ commandx = (scancode->data & 0x0040) >> 6;
+ system = (scancode->data & 0x1f00) >> 8;
+ /* encode data */
+ data = !commandx << 12 | system << 6 | command;
+
+ /* Modulate the data */
+ ret = ir_raw_gen_manchester(&e, max, &ir_rc5_timings, RC5_NBITS,
+ data);
+ if (ret < 0)
+ return ret;
+ } else if (protocols & RC_BIT_RC5X &&
+ !ir_rc5_validate_filter(scancode, 0x1f7f3f)) {
+ /* decode scancode */
+ xdata = (scancode->data & 0x00003f) >> 0;
+ command = (scancode->data & 0x003f00) >> 8;
+ commandx = (scancode->data & 0x004000) >> 14;
+ system = (scancode->data & 0x1f0000) >> 16;
+ /* commandx and system overlap, bits must match when encoded */
+ if (commandx == (system & 0x1))
+ return -EINVAL;
+ /* encode data */
+ data = 1 << 18 | system << 12 | command << 6 | xdata;
+
+ /* Modulate the data */
+ ret = ir_raw_gen_manchester(&e, max, &ir_rc5x_timings[0],
+ CHECK_RC5X_NBITS,
+ data >> (RC5X_NBITS-CHECK_RC5X_NBITS));
+ if (ret < 0)
+ return ret;
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc5x_timings[1],
+ RC5X_NBITS - CHECK_RC5X_NBITS,
+ data);
+ if (ret < 0)
+ return ret;
+ } else if (protocols & RC_BIT_RC5_SZ &&
+ !ir_rc5_validate_filter(scancode, 0x2fff)) {
+ /* RC5-SZ scancode is raw enough for Manchester as it is */
+ ret = ir_raw_gen_manchester(&e, max, &ir_rc5_sz_timings,
+ RC5_SZ_NBITS, scancode->data & 0x2fff);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+
+ return e - events;
+}
+
static struct ir_raw_handler rc5_handler = {
.protocols = RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ,
.decode = ir_rc5_decode,
+ .encode = ir_rc5_encode,
};
static int __init ir_rc5_decode_init(void)
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index d16bc67..f9c70ba 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -291,11 +291,133 @@ out:
return -EINVAL;
}
+static struct ir_raw_timings_manchester ir_rc6_timings[4] = {
+ {
+ .leader = RC6_PREFIX_PULSE,
+ .pulse_space_start = 0,
+ .clock = RC6_UNIT,
+ .invert = 1,
+ .trailer_space = RC6_PREFIX_SPACE,
+ },
+ {
+ .clock = RC6_UNIT,
+ .invert = 1,
+ },
+ {
+ .clock = RC6_UNIT * 2,
+ .invert = 1,
+ },
+ {
+ .clock = RC6_UNIT,
+ .invert = 1,
+ .trailer_space = RC6_SUFFIX_SPACE,
+ },
+};
+
+static int ir_rc6_validate_filter(const struct rc_scancode_filter *scancode,
+ unsigned int important_bits)
+{
+ /* all important bits of scancode should be set in mask */
+ if (~scancode->mask & important_bits)
+ return -EINVAL;
+ /* extra bits in mask should be zero in data */
+ if (scancode->mask & scancode->data & ~important_bits)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * ir_rc6_encode() - Encode a scancode as a stream of raw events
+ *
+ * @protocols: allowed protocols
+ * @scancode: scancode filter describing scancode (helps distinguish between
+ * protocol subtypes when scancode is ambiguous)
+ * @events: array of raw ir events to write into
+ * @max: maximum size of @events
+ *
+ * Returns: The number of events written.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * encoding. In this case all @max events will have been written.
+ * -EINVAL if the scancode is ambiguous or invalid.
+ */
+static int ir_rc6_encode(u64 protocols,
+ const struct rc_scancode_filter *scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ int ret;
+ struct ir_raw_event *e = events;
+
+ if (protocols & RC_BIT_RC6_0 &&
+ !ir_rc6_validate_filter(scancode, 0xffff)) {
+
+ /* Modulate the preamble */
+ ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Modulate the header (Start Bit & Mode-0) */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[1],
+ RC6_HEADER_NBITS, (1 << 3));
+ if (ret < 0)
+ return ret;
+
+ /* Modulate Trailer Bit */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[2], 1, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Modulate rest of the data */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[3], RC6_0_NBITS,
+ scancode->data);
+ if (ret < 0)
+ return ret;
+
+ } else if (protocols & (RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
+ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE) &&
+ !ir_rc6_validate_filter(scancode, 0x8fffffff)) {
+
+ /* Modulate the preamble */
+ ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Modulate the header (Start Bit & Header-version 6 */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[1],
+ RC6_HEADER_NBITS, (1 << 3 | 6));
+ if (ret < 0)
+ return ret;
+
+ /* Modulate Trailer Bit */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[2], 1, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Modulate rest of the data */
+ ret = ir_raw_gen_manchester(&e, max - (e - events),
+ &ir_rc6_timings[3],
+ fls(scancode->mask),
+ scancode->data);
+ if (ret < 0)
+ return ret;
+
+ } else {
+ return -EINVAL;
+ }
+
+ return e - events;
+}
+
static struct ir_raw_handler rc6_handler = {
.protocols = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
RC_BIT_RC6_MCE,
.decode = ir_rc6_decode,
+ .encode = ir_rc6_encode,
};
static int __init ir_rc6_decode_init(void)
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index d12dc3d..58ef06f 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/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/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index abf6079..fbbd3bb 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/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/drivers/media/rc/keymaps/rc-technisat-ts35.c b/drivers/media/rc/keymaps/rc-technisat-ts35.c
new file mode 100644
index 0000000..3328cbe
--- /dev/null
+++ b/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 <jan@kloetzke.net>
+ *
+ * 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 <media/rc-map.h>
+#include <linux/module.h>
+
+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/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c
new file mode 100644
index 0000000..7958f45
--- /dev/null
+++ b/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 <liplianin@me.by>
+ *
+ * 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 <media/rc-map.h>
+#include <linux/module.h>
+
+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/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c
new file mode 100644
index 0000000..1e096bb
--- /dev/null
+++ b/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 <media/rc-map.h>
+#include <linux/module.h>
+
+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/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c b/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c
new file mode 100644
index 0000000..202500c
--- /dev/null
+++ b/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 <liplianin@me.by>
+ *
+ * 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 <media/rc-map.h>
+#include <linux/module.h>
+
+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/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 85af7a8..baeb597 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -526,6 +526,130 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
return 0;
}
+static int nvt_write_wakeup_codes(struct rc_dev *dev,
+ const u8 *wakeup_sample_buf, int count)
+{
+ int i = 0;
+ u8 reg, reg_learn_mode;
+ unsigned long flags;
+ struct nvt_dev *nvt = dev->priv;
+
+ nvt_dbg_wake("writing wakeup samples");
+
+ reg = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
+ reg_learn_mode = reg & ~CIR_WAKE_IRCON_MODE0;
+ reg_learn_mode |= CIR_WAKE_IRCON_MODE1;
+
+ /* Lock the learn area to prevent racing with wake-isr */
+ spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+ /* Enable fifo writes */
+ nvt_cir_wake_reg_write(nvt, reg_learn_mode, CIR_WAKE_IRCON);
+
+ /* Clear cir wake rx fifo */
+ nvt_clear_cir_wake_fifo(nvt);
+
+ if (count > WAKE_FIFO_LEN) {
+ nvt_dbg_wake("HW FIFO too small for all wake samples");
+ count = WAKE_FIFO_LEN;
+ }
+
+ if (count)
+ pr_info("Wake samples (%d) =", count);
+ else
+ pr_info("Wake sample fifo cleared");
+
+ /* Write wake samples to fifo */
+ for (i = 0; i < count; i++) {
+ pr_cont(" %02x", wakeup_sample_buf[i]);
+ nvt_cir_wake_reg_write(nvt, wakeup_sample_buf[i],
+ CIR_WAKE_WR_FIFO_DATA);
+ }
+ pr_cont("\n");
+
+ /* Switch cir to wakeup mode and disable fifo writing */
+ nvt_cir_wake_reg_write(nvt, reg, CIR_WAKE_IRCON);
+
+ /* Set number of bytes needed for wake */
+ nvt_cir_wake_reg_write(nvt, count ? count :
+ CIR_WAKE_FIFO_CMP_BYTES,
+ CIR_WAKE_FIFO_CMP_DEEP);
+
+ spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+ return 0;
+}
+
+static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ u8 *reg_buf;
+ u8 buf_val;
+ int i, ret, count;
+ unsigned int val;
+ struct ir_raw_event *raw;
+ bool complete;
+
+ /* Require both mask and data to be set before actually committing */
+ if (!sc_filter->mask || !sc_filter->data)
+ return 0;
+
+ raw = kmalloc_array(WAKE_FIFO_LEN, sizeof(*raw), GFP_KERNEL);
+ if (!raw)
+ return -ENOMEM;
+
+ ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter,
+ raw, WAKE_FIFO_LEN);
+ complete = (ret != -ENOBUFS);
+ if (!complete)
+ ret = WAKE_FIFO_LEN;
+ else if (ret < 0)
+ goto out_raw;
+
+ reg_buf = kmalloc_array(WAKE_FIFO_LEN, sizeof(*reg_buf), GFP_KERNEL);
+ if (!reg_buf) {
+ ret = -ENOMEM;
+ goto out_raw;
+ }
+
+ /* Inspect the ir samples */
+ for (i = 0, count = 0; i < ret && count < WAKE_FIFO_LEN; ++i) {
+ val = NS_TO_US((raw[i]).duration) / SAMPLE_PERIOD;
+
+ /* Split too large values into several smaller ones */
+ while (val > 0 && count < WAKE_FIFO_LEN) {
+
+ /* Skip last value for better comparison tolerance */
+ if (complete && i == ret - 1 && val < BUF_LEN_MASK)
+ break;
+
+ /* Clamp values to BUF_LEN_MASK at most */
+ buf_val = (val > BUF_LEN_MASK) ? BUF_LEN_MASK : val;
+
+ reg_buf[count] = buf_val;
+ val -= buf_val;
+ if ((raw[i]).pulse)
+ reg_buf[count] |= BUF_PULSE_BIT;
+ count++;
+ }
+ }
+
+ ret = nvt_write_wakeup_codes(dev, reg_buf, count);
+
+ kfree(reg_buf);
+out_raw:
+ kfree(raw);
+
+ return ret;
+}
+
+/* Dummy implementation. nuvoton is agnostic to the protocol used */
+static int nvt_ir_raw_change_wakeup_protocol(struct rc_dev *dev,
+ u64 *rc_type)
+{
+ return 0;
+}
+
/*
* nvt_tx_ir
*
@@ -1043,11 +1167,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
/* Set up the rc device */
rdev->priv = nvt;
rdev->driver_type = RC_DRIVER_IR_RAW;
+ rdev->encode_wakeup = true;
rdev->allowed_protocols = RC_BIT_ALL;
rdev->open = nvt_open;
rdev->close = nvt_close;
rdev->tx_ir = nvt_tx_ir;
rdev->s_tx_carrier = nvt_set_tx_carrier;
+ rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter;
+ rdev->change_wakeup_protocol = nvt_ir_raw_change_wakeup_protocol;
rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
rdev->input_phys = "nuvoton/cir0";
rdev->input_id.bustype = BUS_HOST;
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index e1cf23c..9d0e161 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -63,6 +63,7 @@ static int debug;
*/
#define TX_BUF_LEN 256
#define RX_BUF_LEN 32
+#define WAKE_FIFO_LEN 67
struct nvt_dev {
struct pnp_dev *pdev;
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index b68d4f76..4b994aa 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -25,6 +25,8 @@ struct ir_raw_handler {
u64 protocols; /* which are handled by this handler */
int (*decode)(struct rc_dev *dev, struct ir_raw_event event);
+ int (*encode)(u64 protocols, const struct rc_scancode_filter *scancode,
+ struct ir_raw_event *events, unsigned int max);
/* These two should only be used by the lirc decoder */
int (*raw_register)(struct rc_dev *dev);
@@ -150,10 +152,44 @@ static inline bool is_timing_event(struct ir_raw_event ev)
#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000)
#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space")
+/* functions for IR encoders */
+
+static inline void init_ir_raw_event_duration(struct ir_raw_event *ev,
+ unsigned int pulse,
+ u32 duration)
+{
+ init_ir_raw_event(ev);
+ ev->duration = duration;
+ ev->pulse = pulse;
+}
+
+/**
+ * struct ir_raw_timings_manchester - Manchester coding timings
+ * @leader: duration of leader pulse (if any) 0 if continuing
+ * existing signal (see @pulse_space_start)
+ * @pulse_space_start: 1 for starting with pulse (0 for starting with space)
+ * @clock: duration of each pulse/space in ns
+ * @invert: if set clock logic is inverted
+ * (0 = space + pulse, 1 = pulse + space)
+ * @trailer_space: duration of trailer space in ns
+ */
+struct ir_raw_timings_manchester {
+ unsigned int leader;
+ unsigned int pulse_space_start:1;
+ unsigned int clock;
+ unsigned int invert:1;
+ unsigned int trailer_space;
+};
+
+int ir_raw_gen_manchester(struct ir_raw_event **ev, unsigned int max,
+ const struct ir_raw_timings_manchester *timings,
+ unsigned int n, unsigned int data);
+
/*
* Routines from rc-raw.c to be used internally and by decoders
*/
u64 ir_raw_get_allowed_protocols(void);
+u64 ir_raw_get_encode_protocols(void);
int ir_raw_event_register(struct rc_dev *dev);
void ir_raw_event_unregister(struct rc_dev *dev);
int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index b732ac6..b9e4645 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -30,6 +30,7 @@ static LIST_HEAD(ir_raw_client_list);
static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
+static u64 encode_protocols;
static int ir_raw_event_thread(void *data)
{
@@ -240,12 +241,146 @@ ir_raw_get_allowed_protocols(void)
return protocols;
}
+/* used internally by the sysfs interface */
+u64
+ir_raw_get_encode_protocols(void)
+{
+ u64 protocols;
+
+ mutex_lock(&ir_raw_handler_lock);
+ protocols = encode_protocols;
+ mutex_unlock(&ir_raw_handler_lock);
+ return protocols;
+}
+
static int change_protocol(struct rc_dev *dev, u64 *rc_type)
{
/* the caller will update dev->enabled_protocols */
return 0;
}
+/**
+ * ir_raw_gen_manchester() - Encode data with Manchester (bi-phase) modulation.
+ * @ev: Pointer to pointer to next free event. *@ev is incremented for
+ * each raw event filled.
+ * @max: Maximum number of raw events to fill.
+ * @timings: Manchester modulation timings.
+ * @n: Number of bits of data.
+ * @data: Data bits to encode.
+ *
+ * Encodes the @n least significant bits of @data using Manchester (bi-phase)
+ * modulation with the timing characteristics described by @timings, writing up
+ * to @max raw IR events using the *@ev pointer.
+ *
+ * Returns: 0 on success.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * full encoded data. In this case all @max events will have been
+ * written.
+ */
+int ir_raw_gen_manchester(struct ir_raw_event **ev, unsigned int max,
+ const struct ir_raw_timings_manchester *timings,
+ unsigned int n, unsigned int data)
+{
+ bool need_pulse;
+ unsigned int i;
+ int ret = -ENOBUFS;
+
+ i = 1 << (n - 1);
+
+ if (timings->leader) {
+ if (!max--)
+ return ret;
+ if (timings->pulse_space_start) {
+ init_ir_raw_event_duration((*ev)++, 1, timings->leader);
+
+ if (!max--)
+ return ret;
+ init_ir_raw_event_duration((*ev), 0, timings->leader);
+ } else {
+ init_ir_raw_event_duration((*ev), 1, timings->leader);
+ }
+ i >>= 1;
+ } else {
+ /* continue existing signal */
+ --(*ev);
+ }
+ /* from here on *ev will point to the last event rather than the next */
+
+ while (n && i > 0) {
+ need_pulse = !(data & i);
+ if (timings->invert)
+ need_pulse = !need_pulse;
+ if (need_pulse == !!(*ev)->pulse) {
+ (*ev)->duration += timings->clock;
+ } else {
+ if (!max--)
+ goto nobufs;
+ init_ir_raw_event_duration(++(*ev), need_pulse,
+ timings->clock);
+ }
+
+ if (!max--)
+ goto nobufs;
+ init_ir_raw_event_duration(++(*ev), !need_pulse,
+ timings->clock);
+ i >>= 1;
+ }
+
+ if (timings->trailer_space) {
+ if (!(*ev)->pulse)
+ (*ev)->duration += timings->trailer_space;
+ else if (!max--)
+ goto nobufs;
+ else
+ init_ir_raw_event_duration(++(*ev), 0,
+ timings->trailer_space);
+ }
+
+ ret = 0;
+nobufs:
+ /* point to the next event rather than last event before returning */
+ ++(*ev);
+ return ret;
+}
+EXPORT_SYMBOL(ir_raw_gen_manchester);
+
+/**
+ * ir_raw_encode_scancode() - Encode a scancode as raw events
+ *
+ * @protocols: permitted protocols
+ * @scancode: scancode filter describing a single scancode
+ * @events: array of raw events to write into
+ * @max: max number of raw events
+ *
+ * Attempts to encode the scancode as raw events.
+ *
+ * Returns: The number of events written.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * encoding. In this case all @max events will have been written.
+ * -EINVAL if the scancode is ambiguous or invalid, or if no
+ * compatible encoder was found.
+ */
+int ir_raw_encode_scancode(u64 protocols,
+ const struct rc_scancode_filter *scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ struct ir_raw_handler *handler;
+ int ret = -EINVAL;
+
+ mutex_lock(&ir_raw_handler_lock);
+ list_for_each_entry(handler, &ir_raw_handler_list, list) {
+ if (handler->protocols & protocols && handler->encode) {
+ ret = handler->encode(protocols, scancode, events, max);
+ if (ret >= 0 || ret == -ENOBUFS)
+ break;
+ }
+ }
+ mutex_unlock(&ir_raw_handler_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(ir_raw_encode_scancode);
+
/*
* Used to (un)register raw event clients
*/
@@ -328,6 +463,8 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_register(raw->dev);
available_protocols |= ir_raw_handler->protocols;
+ if (ir_raw_handler->encode)
+ encode_protocols |= ir_raw_handler->protocols;
mutex_unlock(&ir_raw_handler_lock);
return 0;
@@ -344,6 +481,8 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_unregister(raw->dev);
available_protocols &= ~ir_raw_handler->protocols;
+ if (ir_raw_handler->encode)
+ encode_protocols &= ~ir_raw_handler->protocols;
mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 63dace8..d8bdf63 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -26,6 +26,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <media/rc-core.h>
#define DRIVER_NAME "rc-loopback"
@@ -176,6 +177,39 @@ static int loop_set_carrier_report(struct rc_dev *dev, int enable)
return 0;
}
+static int loop_set_wakeup_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ static const unsigned int max = 512;
+ struct ir_raw_event *raw;
+ int ret;
+ int i;
+
+ /* fine to disable filter */
+ if (!sc_filter->mask)
+ return 0;
+
+ /* encode the specified filter and loop it back */
+ raw = kmalloc_array(max, sizeof(*raw), GFP_KERNEL);
+ ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter,
+ raw, max);
+ /* still loop back the partial raw IR even if it's incomplete */
+ if (ret == -ENOBUFS)
+ ret = max;
+ if (ret >= 0) {
+ /* do the loopback */
+ for (i = 0; i < ret; ++i)
+ ir_raw_event_store(dev, &raw[i]);
+ ir_raw_event_handle(dev);
+
+ ret = 0;
+ }
+
+ kfree(raw);
+
+ return ret;
+}
+
static int __init loop_init(void)
{
struct rc_dev *rc;
@@ -195,6 +229,7 @@ static int __init loop_init(void)
rc->map_name = RC_MAP_EMPTY;
rc->priv = &loopdev;
rc->driver_type = RC_DRIVER_IR_RAW;
+ rc->encode_wakeup = true;
rc->allowed_protocols = RC_BIT_ALL;
rc->timeout = 100 * 1000 * 1000; /* 100 ms */
rc->min_timeout = 1;
@@ -209,6 +244,7 @@ static int __init loop_init(void)
rc->s_idle = loop_set_idle;
rc->s_learning_mode = loop_set_learning_mode;
rc->s_carrier_report = loop_set_carrier_report;
+ rc->s_wakeup_filter = loop_set_wakeup_filter;
loopdev.txmask = RXMASK_REGULAR;
loopdev.txcarrier = 36000;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index f8c5e47..9d015db 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -746,7 +746,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);
@@ -865,6 +865,8 @@ static ssize_t show_protocols(struct device *device,
} else {
enabled = dev->enabled_wakeup_protocols;
allowed = dev->allowed_wakeup_protocols;
+ if (dev->encode_wakeup && !allowed)
+ allowed = ir_raw_get_encode_protocols();
}
mutex_unlock(&dev->lock);
@@ -1406,13 +1408,16 @@ int rc_register_device(struct rc_dev *dev)
path ? path : "N/A");
kfree(path);
- if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ if (dev->driver_type == RC_DRIVER_IR_RAW || dev->encode_wakeup) {
/* Load raw decoders, if they aren't already */
if (!raw_init) {
IR_dprintk(1, "Loading raw decoders\n");
ir_raw_init();
raw_init = true;
}
+ }
+
+ if (dev->driver_type == RC_DRIVER_IR_RAW) {
/* calls ir_register_device so unlock mutex here*/
mutex_unlock(&dev->lock);
rc = ir_raw_event_register(dev);
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index c4def66..ec74244 100644
--- a/drivers/media/rc/redrat3.c
+++ b/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/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index 0e758ae..37d0401 100644
--- a/drivers/media/rc/st_rc.c
+++ b/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/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index bf4a442..5a17cb8 100644
--- a/drivers/media/rc/streamzap.c
+++ b/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/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 983510d..8294af9 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -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/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 510239f..03538f8 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -19,164 +19,176 @@
*/
#include "e4000_priv.h"
-#include <linux/math64.h>
-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,176 @@ 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, &regmap_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, &regmap_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/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
index e74b8b2..aa9340c 100644
--- a/drivers/media/tuners/e4000.h
+++ b/drivers/media/tuners/e4000.h
@@ -21,7 +21,6 @@
#ifndef E4000_H
#define E4000_H
-#include <linux/kconfig.h>
#include "dvb_frontend.h"
/*
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index cb00704..d6d5d11 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -22,17 +22,20 @@
#define E4000_PRIV_H
#include "e4000.h"
+#include <linux/math64.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <linux/regmap.h>
-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/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c
index b416231..522690d 100644
--- a/drivers/media/tuners/fc0013.c
+++ b/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/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
index f0c9c42..12f916e 100644
--- a/drivers/media/tuners/fc2580.c
+++ b/drivers/media/tuners/fc2580.c
@@ -20,535 +20,628 @@
#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 = &reg,
- }, {
- .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, &regmap_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 = {
+ .owner = THIS_MODULE,
+ .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 <crope@iki.fi>");
diff --git a/drivers/media/tuners/fc2580.h b/drivers/media/tuners/fc2580.h
index b1ce677..862ea46 100644
--- a/drivers/media/tuners/fc2580.h
+++ b/drivers/media/tuners/fc2580.h
@@ -21,32 +21,26 @@
#ifndef FC2580_H
#define FC2580_H
-#include <linux/kconfig.h>
#include "dvb_frontend.h"
+#include <media/v4l2-subdev.h>
+#include <linux/i2c.h>
-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/drivers/media/tuners/fc2580_priv.h b/drivers/media/tuners/fc2580_priv.h
index 646c994..031a43d 100644
--- a/drivers/media/tuners/fc2580_priv.h
+++ b/drivers/media/tuners/fc2580_priv.h
@@ -22,6 +22,9 @@
#define FC2580_PRIV_H
#include "fc2580.h"
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/regmap.h>
#include <linux/math64.h>
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/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c
index 74cfc3c..b533240 100644
--- a/drivers/media/tuners/msi001.c
+++ b/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,27 @@ 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/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c
index bc419f8..ae8cbec 100644
--- a/drivers/media/tuners/qt1010.c
+++ b/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/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index 71159a5..a7a8452 100644
--- a/drivers/media/tuners/r820t.c
+++ b/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/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index d74ae26..a6245ef 100644
--- a/drivers/media/tuners/si2157.c
+++ b/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;
@@ -201,9 +202,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 +228,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 +307,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 +369,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 +416,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;
diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h
index a564c4a..4db97ab 100644
--- a/drivers/media/tuners/si2157.h
+++ b/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/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index cd8fa5b..ecc463d 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/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/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c
index 83a6240..d4f6ca0 100644
--- a/drivers/media/tuners/tua9001.c
+++ b/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 <crope@iki.fi>
*
@@ -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,108 @@ 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, &regmap_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 = {
+ .owner = THIS_MODULE,
+ .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 <crope@iki.fi>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/tua9001.h b/drivers/media/tuners/tua9001.h
index 2c3375c..7b05481 100644
--- a/drivers/media/tuners/tua9001.h
+++ b/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 <crope@iki.fi>
*
@@ -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 <linux/kconfig.h>
#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/drivers/media/tuners/tua9001_priv.h b/drivers/media/tuners/tua9001_priv.h
index 73cc1ce..bc406c5 100644
--- a/drivers/media/tuners/tua9001_priv.h
+++ b/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 <crope@iki.fi>
*
@@ -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 <linux/math64.h>
+#include <linux/regmap.h>
+
+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/drivers/media/tuners/tuner-i2c.h b/drivers/media/tuners/tuner-i2c.h
index 18f0056..bda67a5 100644
--- a/drivers/media/tuners/tuner-i2c.h
+++ b/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/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c
index d12f5e4..4e941f0 100644
--- a/drivers/media/tuners/tuner-xc2028.c
+++ b/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/drivers/media/usb/as102/as102_drv.c b/drivers/media/usb/as102/as102_drv.c
index 8be1474..9dd7c7c 100644
--- a/drivers/media/usb/as102/as102_drv.c
+++ b/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/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index edc2735..6b469e8 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/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/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 082ae6b..0934024 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 983ea83..47a98a2 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 39e8879..4919137 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index fe00da1..4a117a5 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index e42bde0..a2fd49b 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 610d567..66ee161 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index 80261ac..a08014d 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/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/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index c261e16..c6ff896 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/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);
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 00d3bce..54790fb 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/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/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index 16c0b7d..95a7388 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/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/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h
index 3a6f3ad..1db1bb0 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.h
+++ b/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/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index 80a29f5..6e02a15 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/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/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index cdf59bc..0376c09 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/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;
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 5de6f7c..4cc55b3 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/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;
@@ -337,6 +393,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 +857,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 +896,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 +930,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;
}
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
index ecefa5c..ea37536 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
+++ b/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/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 895441f..c3cac4c 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -217,7 +217,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 +232,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 +363,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 +485,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 +547,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 +689,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 +719,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 +860,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 +872,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 +942,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 +964,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 +1104,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 +1182,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 +1219,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 +1257,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 +1300,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 +1321,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;
@@ -1764,6 +1907,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/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 1b5d7ff..9f6115a 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/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
@@ -76,6 +78,7 @@ struct rtl28xxu_dev {
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/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c
index 740f3f4..ac97075 100644
--- a/drivers/media/usb/dvb-usb/af9005-fe.c
+++ b/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/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c
index 0df52ab..92e47d6 100644
--- a/drivers/media/usb/dvb-usb/az6027.c
+++ b/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/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c
index c890fe4..b3ec743 100644
--- a/drivers/media/usb/dvb-usb/cinergyT2-fe.c
+++ b/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/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index ffc3704..ab71511 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/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/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h
index 927617d..8fd8f5b 100644
--- a/drivers/media/usb/dvb-usb/dib0700.h
+++ b/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/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 2b40393..0d248ce 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -655,10 +655,20 @@ out:
struct dib0700_rc_response {
u8 report_id;
u8 data_state;
- u8 system;
- u8 not_system;
- u8 data;
- u8 not_data;
+ union {
+ struct {
+ u8 system;
+ u8 not_system;
+ u8 data;
+ u8 not_data;
+ } nec;
+ struct {
+ u8 not_used;
+ u8 system;
+ u8 data;
+ u8 not_data;
+ } rc5;
+ };
};
#define RC_MSG_SIZE_V1_20 6
@@ -694,8 +704,8 @@ static void dib0700_rc_urb_completion(struct urb *purb)
deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
poll_reply->report_id, poll_reply->data_state,
- poll_reply->system, poll_reply->not_system,
- poll_reply->data, poll_reply->not_data,
+ poll_reply->nec.system, poll_reply->nec.not_system,
+ poll_reply->nec.data, poll_reply->nec.not_data,
purb->actual_length);
switch (d->props.rc.core.protocol) {
@@ -704,30 +714,30 @@ static void dib0700_rc_urb_completion(struct urb *purb)
toggle = 0;
/* NEC protocol sends repeat code as 0 0 0 FF */
- if (poll_reply->system == 0x00 &&
- poll_reply->not_system == 0x00 &&
- poll_reply->data == 0x00 &&
- poll_reply->not_data == 0xff) {
+ if (poll_reply->nec.system == 0x00 &&
+ poll_reply->nec.not_system == 0x00 &&
+ poll_reply->nec.data == 0x00 &&
+ poll_reply->nec.not_data == 0xff) {
poll_reply->data_state = 2;
break;
}
- if ((poll_reply->data ^ poll_reply->not_data) != 0xff) {
+ if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
deb_data("NEC32 protocol\n");
- keycode = RC_SCANCODE_NEC32(poll_reply->system << 24 |
- poll_reply->not_system << 16 |
- poll_reply->data << 8 |
- poll_reply->not_data);
- } else if ((poll_reply->system ^ poll_reply->not_system) != 0xff) {
+ keycode = RC_SCANCODE_NEC32(poll_reply->nec.system << 24 |
+ poll_reply->nec.not_system << 16 |
+ poll_reply->nec.data << 8 |
+ poll_reply->nec.not_data);
+ } else if ((poll_reply->nec.system ^ poll_reply->nec.not_system) != 0xff) {
deb_data("NEC extended protocol\n");
- keycode = RC_SCANCODE_NECX(poll_reply->system << 8 |
- poll_reply->not_system,
- poll_reply->data);
+ keycode = RC_SCANCODE_NECX(poll_reply->nec.system << 8 |
+ poll_reply->nec.not_system,
+ poll_reply->nec.data);
} else {
deb_data("NEC normal protocol\n");
- keycode = RC_SCANCODE_NEC(poll_reply->system,
- poll_reply->data);
+ keycode = RC_SCANCODE_NEC(poll_reply->nec.system,
+ poll_reply->nec.data);
}
break;
@@ -735,19 +745,19 @@ static void dib0700_rc_urb_completion(struct urb *purb)
deb_data("RC5 protocol\n");
protocol = RC_TYPE_RC5;
toggle = poll_reply->report_id;
- keycode = RC_SCANCODE_RC5(poll_reply->system, poll_reply->data);
+ keycode = RC_SCANCODE_RC5(poll_reply->rc5.system, poll_reply->rc5.data);
+
+ if ((poll_reply->rc5.data ^ poll_reply->rc5.not_data) != 0xff) {
+ /* Key failed integrity check */
+ err("key failed integrity check: %02x %02x %02x %02x",
+ poll_reply->rc5.not_used, poll_reply->rc5.system,
+ poll_reply->rc5.data, poll_reply->rc5.not_data);
+ goto resubmit;
+ }
break;
}
- if ((poll_reply->data + poll_reply->not_data) != 0xff) {
- /* Key failed integrity check */
- err("key failed integrity check: %02x %02x %02x %02x",
- poll_reply->system, poll_reply->not_system,
- poll_reply->data, poll_reply->not_data);
- goto resubmit;
- }
-
rc_keydown(d->rc_dev, protocol, keycode, toggle);
resubmit:
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index d7d55a2..7ed4964 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/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,
@@ -3945,7 +3948,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
}, {
- .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,
@@ -3998,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,
@@ -4043,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,
@@ -4054,7 +4057,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -4125,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,
@@ -4136,7 +4138,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
@@ -4171,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,
@@ -4182,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,
@@ -4195,7 +4195,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
}},
- .size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -4230,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,
@@ -4241,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,
@@ -4254,7 +4252,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
}},
- .size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -4298,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,
@@ -4309,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,
@@ -4322,7 +4318,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
}},
- .size_of_priv = sizeof(struct dib0700_adapter_state),
}
},
@@ -4349,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,
@@ -4360,8 +4355,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv = sizeof(struct
- dib0700_adapter_state),
},
},
@@ -4419,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),
},
},
@@ -4457,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),
},
},
@@ -4485,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,
@@ -4496,8 +4485,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4537,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,
@@ -4548,8 +4535,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4583,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,
@@ -4594,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,
@@ -4609,8 +4592,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4636,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,
@@ -4648,8 +4629,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4675,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,
@@ -4687,8 +4666,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4714,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,
@@ -4726,8 +4703,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4753,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,
@@ -4765,8 +4740,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4792,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,
@@ -4804,8 +4777,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4831,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,
@@ -4843,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,
@@ -4859,8 +4828,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}},
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4886,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),
},
},
@@ -4923,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,
@@ -4935,9 +4900,6 @@ struct dvb_usb_device_properties dib0700_devices[] = {
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
} },
-
- .size_of_priv =
- sizeof(struct dib0700_adapter_state),
},
},
@@ -4963,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,
@@ -4976,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/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c
index 3d81daa..8637ad1 100644
--- a/drivers/media/usb/dvb-usb/dtt200u-fe.c
+++ b/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/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index f1f357f..14ef25d 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/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/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c
index d56f927..8ec92fb 100644
--- a/drivers/media/usb/dvb-usb/friio-fe.c
+++ b/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/drivers/media/usb/dvb-usb/gp8psk-fe.c b/drivers/media/usb/dvb-usb/gp8psk-fe.c
index 67957dd..db6eb79 100644
--- a/drivers/media/usb/dvb-usb/gp8psk-fe.c
+++ b/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/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c
index 14a2119..2566d2f 100644
--- a/drivers/media/usb/dvb-usb/opera1.c
+++ b/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/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c
index 5801ae7..03f334d 100644
--- a/drivers/media/usb/dvb-usb/technisat-usb2.c
+++ b/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 */
diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c
index 5eab468..d361a72 100644
--- a/drivers/media/usb/dvb-usb/vp702x-fe.c
+++ b/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/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c
index 22cf9f9..ee1e19e 100644
--- a/drivers/media/usb/dvb-usb/vp702x.c
+++ b/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/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c
index b8825b1..e708afc 100644
--- a/drivers/media/usb/dvb-usb/vp7045-fe.c
+++ b/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/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index a4b22c2..ed0b3a8 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/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/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index a5b22c5..a382483 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/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,16 +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) */
};
@@ -1331,16 +1322,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 +1521,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 +1644,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 +1705,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 +1794,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 +1810,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/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 14eba9c..4397ce5 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -839,7 +839,6 @@ static int get_ressource(enum v4l2_buf_type f_type)
return EM28XX_RESOURCE_VBI;
default:
BUG();
- return 0;
}
}
diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c
index 95cffb7..0ab81ec 100644
--- a/drivers/media/usb/go7007/go7007-driver.c
+++ b/drivers/media/usb/go7007/go7007-driver.c
@@ -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,6 +458,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf
go->next_seq++;
return vb;
}
+ bytesused = &vb->vb.v4l2_planes[0].bytesused;
vb->vb.v4l2_buf.sequence = go->next_seq++;
if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE)
diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c
index 3f986e1..4857c46 100644
--- a/drivers/media/usb/go7007/go7007-usb.c
+++ b/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/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index d6bf982..c57207e 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -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;
diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c
index bb84668..5c2a495 100644
--- a/drivers/media/usb/go7007/s2250-board.c
+++ b/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,
};
/* --------------------------------------------------------------------------*/
diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c
index 05f406dea..790baed 100644
--- a/drivers/media/usb/gspca/benq.c
+++ b/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/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c
index 39b6b2e..c75b738 100644
--- a/drivers/media/usb/gspca/sn9c2028.c
+++ b/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,7 +138,7 @@ 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;
}
@@ -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/drivers/media/usb/gspca/sn9c2028.h b/drivers/media/usb/gspca/sn9c2028.h
index 8fd1d3e..f85bc10 100644
--- a/drivers/media/usb/gspca/sn9c2028.h
+++ b/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/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c
index c69b45d..fd1c870 100644
--- a/drivers/media/usb/gspca/sonixj.c
+++ b/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/drivers/media/usb/gspca/stk014.c b/drivers/media/usb/gspca/stk014.c
index b0c70fe..d324d00 100644
--- a/drivers/media/usb/gspca/stk014.c
+++ b/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/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c
index a41aa78..d5ed9d3 100644
--- a/drivers/media/usb/gspca/xirlink_cit.c
+++ b/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/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c
index d3e1b6d..c5d8ee6 100644
--- a/drivers/media/usb/gspca/zc3xx.c
+++ b/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/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index efc761c..3f276d9 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -1,4 +1,5 @@
/*
+ * Mirics MSi2500 driver
* Mirics MSi3101 SDR Dongle driver
*
* Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
@@ -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)
@@ -119,7 +116,7 @@ struct msi2500_frame_buf {
struct list_head list;
};
-struct msi2500_state {
+struct msi2500_dev {
struct device *dev;
struct video_device vdev;
struct v4l2_device v4l2_dev;
@@ -158,19 +155,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 +253,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 +265,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 +293,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 +315,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 +354,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 +376,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 +407,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,18 +421,18 @@ 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);
+ flen = msi2500_convert_stream(dev, ptr, iso_buf, flen);
vb2_set_plane_payload(&fbuf->vb, 0, flen);
vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
}
@@ -441,66 +440,66 @@ static void msi2500_isoc_handler(struct urb *urb)
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 +507,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 +540,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 +556,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);
}
- 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 +614,46 @@ 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 struct v4l2_format *fmt,
+ 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 msi2500_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct msi2500_frame_buf *buf = container_of(vb,
+ struct msi2500_frame_buf,
+ vb);
unsigned long flags;
/* Check the device has not disconnected between prep and queuing */
- if (unlikely(!s->udev)) {
+ if (unlikely(!dev->udev)) {
vb2_buffer_done(&buf->vb, 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 +666,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 +674,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;
@@ -728,6 +730,21 @@ static int msi2500_set_usb_adc(struct msi2500_state *s)
}
/*
+ * Fractional-N synthesizer
+ *
+ * +----------------------------------------+
+ * v |
+ * Fref +----+ +-------+ +-----+ +------+ +---+
+ * ------> | PD | --> | VCO | --> | /2 | ------> | /N.F | <-- | K |
+ * +----+ +-------+ +-----+ +------+ +---+
+ * |
+ * |
+ * v
+ * +-------+ +-----+ Fout
+ * | /Rout | --> | /12 | ------>
+ * +-------+ +-----+
+ */
+ /*
* Synthesizer config is just a educated guess...
*
* [7:0] 0x03, register address
@@ -754,10 +771,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 +788,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 +845,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 +908,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 +924,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 +970,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 +993,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 +1012,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 +1025,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 +1034,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 +1055,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 +1079,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 +1095,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 +1153,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 +1183,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 +1196,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 +1262,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/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c
index 924fc4c..fd888a6 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-context.c
+++ b/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/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index 930593d..0533ef2 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/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/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c
index 0c08f22..d860344 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-io.c
+++ b/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/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
index cd995b5..614d557 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
+++ b/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/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 749ad56..4d313ed 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -500,6 +500,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,
@@ -645,7 +646,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/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index 77ce9ef..fa5e8bd 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/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/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
index cef7a00..d52d4a8 100644
--- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
+++ b/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/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c
index 15ab584..322b53a 100644
--- a/drivers/media/usb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c
@@ -1431,8 +1431,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/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
index 9c29552..8781335 100644
--- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c
+++ b/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/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index 9d3525f..08fb0f2 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -599,15 +599,18 @@ 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 struct v4l2_format *fmt, unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
{
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;
}
@@ -635,6 +638,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/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index 44b0c28..7c04ef6 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -2390,8 +2390,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/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index 12b403e..1c6d31f 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1061,13 +1061,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;
}
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 5970dd6..4b5b3e8 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1967,8 +1967,6 @@ static void uvc_disconnect(struct usb_interface *intf)
UVC_SC_VIDEOSTREAMING)
return;
- dev->state |= UVC_DEV_DISCONNECTED;
-
uvc_unregister_video(dev);
}
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 87a19f3..f16b9b4 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -270,6 +270,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)
{
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index c4b1ac6..2764f43 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/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/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 20ccc9d..f839654 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/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) {
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 1b594c2..816dd1a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -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;
@@ -635,6 +630,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,
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index ca85031..7433ba5 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/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/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index ba7e21a..f7a01a7 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -89,7 +89,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/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c
index c0e9638..04dc71e 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -25,6 +25,7 @@
#include <linux/videodev2.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dv-timings.h>
+#include <linux/math64.h>
MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions");
@@ -261,6 +262,8 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix,
htot = V4L2_DV_BT_FRAME_WIDTH(bt);
vtot = V4L2_DV_BT_FRAME_HEIGHT(bt);
+ if (bt->interlaced)
+ vtot /= 2;
if (prefix == NULL)
prefix = "";
@@ -281,6 +284,11 @@ 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,
(bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ?
@@ -313,6 +321,7 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings);
#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 */
@@ -337,6 +346,7 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings);
* @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
* @fmt - the resulting timings.
*
* This function will attempt to detect if the given values correspond to a
@@ -348,7 +358,7 @@ EXPORT_SYMBOL_GPL(v4l2_print_dv_timings);
* detection function.
*/
bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync,
- u32 polarities, struct v4l2_dv_timings *fmt)
+ 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;
@@ -365,22 +375,32 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync,
else
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 = (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) {
@@ -436,8 +456,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 +470,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 +529,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 +545,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 +565,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 +587,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 +628,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);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index aa407cb..85de455 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -142,6 +142,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",
@@ -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,
@@ -1103,6 +1105,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 +1291,43 @@ 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;
}
- return -EINVAL;
+ if (ret == 0)
+ v4l_fill_fmtdesc(p);
+ return ret;
}
static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
@@ -1618,6 +1806,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);
@@ -2354,7 +2544,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/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 73824a5..dc853e5 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -427,6 +427,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
*/
@@ -564,8 +583,16 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
if (list_empty(&src_q->done_list))
poll_wait(file, &src_q->done_wq, wait);
- if (list_empty(&dst_q->done_list))
+ 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)
+ return rc | POLLIN | POLLRDNORM;
+
poll_wait(file, &dst_q->done_wq, wait);
+ }
if (m2m_ctx->m2m_dev->m2m_ops->lock)
m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv);
@@ -803,6 +830,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)
{
diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index 83143d3..b27cbb1 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
@@ -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/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 66ada01..93b3154 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -182,6 +182,7 @@ module_param(debug, int, 0644);
V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE)
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
@@ -1153,8 +1154,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.
*
@@ -1205,8 +1207,11 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
atomic_dec(&q->owned_by_drv_count);
spin_unlock_irqrestore(&q->done_lock, flags);
- if (state == VB2_BUF_STATE_QUEUED)
+ if (state == VB2_BUF_STATE_QUEUED) {
+ if (q->start_streaming_called)
+ __enqueue_in_driver(vb);
return;
+ }
/* Inform any processes that may be waiting for buffers */
wake_up(&q->done_wq);
@@ -1237,6 +1242,23 @@ 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
@@ -1247,16 +1269,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
{
unsigned int plane;
- if (V4L2_TYPE_IS_OUTPUT(b->type)) {
- if (WARN_ON_ONCE(b->bytesused == 0)) {
- 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");
- }
- }
-
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
if (b->memory == V4L2_MEMORY_USERPTR) {
for (plane = 0; plane < vb->num_planes; ++plane) {
@@ -1297,6 +1309,9 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
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
@@ -1331,6 +1346,9 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
}
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
@@ -1945,6 +1963,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.
@@ -2100,6 +2123,9 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
q->queued_count--;
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ vb->v4l2_buf.flags & V4L2_BUF_FLAG_LAST)
+ q->last_buffer_dequeued = true;
/* go back to dequeued state */
__vb2_dqbuf(vb);
@@ -2313,6 +2339,7 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
*/
__vb2_queue_cancel(q);
q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type);
+ q->last_buffer_dequeued = false;
dprintk(3, "successful\n");
return 0;
@@ -2655,8 +2682,16 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers)
return res | POLLOUT | POLLWRNORM;
- if (list_empty(&q->done_list))
+ 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.
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 644dec73..94c1e64 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -299,7 +299,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 +317,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);
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 45c708e..7289b81 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -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);
@@ -314,9 +315,11 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
* 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:
@@ -351,7 +354,8 @@ 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);
@@ -502,7 +506,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 +524,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/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 657ab30..2fe4c27 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -287,7 +287,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 +305,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);
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index 38a0458..03929a6 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -21,9 +21,27 @@
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6397/registers.h>
+#define MT6397_RTC_BASE 0xe000
+#define MT6397_RTC_SIZE 0x3e
+
+static const struct resource mt6397_rtc_resources[] = {
+ {
+ .start = MT6397_RTC_BASE,
+ .end = MT6397_RTC_BASE + MT6397_RTC_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MT6397_IRQ_RTC,
+ .end = MT6397_IRQ_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static const struct mfd_cell mt6397_devs[] = {
{
.name = "mt6397-rtc",
+ .num_resources = ARRAY_SIZE(mt6397_rtc_resources),
+ .resources = mt6397_rtc_resources,
.of_compatible = "mediatek,mt6397-rtc",
}, {
.name = "mt6397-regulator",
diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c
index 8b4e20a..b1eac71 100644
--- a/drivers/mmc/host/android-goldfish.c
+++ b/drivers/mmc/host/android-goldfish.c
@@ -42,10 +42,10 @@
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/clk.h>
+#include <linux/scatterlist.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/scatterlist.h>
#include <asm/types.h>
#include <asm/io.h>
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index b16f3cd..e2c0057 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
+#include <linux/backing-dev.h>
#include <linux/bio.h>
#include <linux/pagemap.h>
#include <linux/list.h>
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
index c9eb78f..1a92d30 100644
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -48,6 +48,7 @@
#include <linux/blk-mq.h>
#include <linux/hdreg.h>
#include <linux/scatterlist.h>
+#include <linux/idr.h>
#include <asm/div64.h>
#include "ubi-media.h"
@@ -353,6 +354,8 @@ static struct blk_mq_ops ubiblock_mq_ops = {
.map_queue = blk_mq_map_queue,
};
+static DEFINE_IDR(ubiblock_minor_idr);
+
int ubiblock_create(struct ubi_volume_info *vi)
{
struct ubiblock *dev;
@@ -390,7 +393,13 @@ int ubiblock_create(struct ubi_volume_info *vi)
gd->fops = &ubiblock_ops;
gd->major = ubiblock_major;
- gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL);
+ if (gd->first_minor < 0) {
+ dev_err(disk_to_dev(gd),
+ "block: dynamic minor allocation failed");
+ ret = -ENODEV;
+ goto out_put_disk;
+ }
gd->private_data = dev;
sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
set_capacity(gd, disk_capacity);
@@ -407,7 +416,7 @@ int ubiblock_create(struct ubi_volume_info *vi)
ret = blk_mq_alloc_tag_set(&dev->tag_set);
if (ret) {
dev_err(disk_to_dev(dev->gd), "blk_mq_alloc_tag_set failed");
- goto out_put_disk;
+ goto out_remove_minor;
}
dev->rq = blk_mq_init_queue(&dev->tag_set);
@@ -445,6 +454,8 @@ out_free_queue:
blk_cleanup_queue(dev->rq);
out_free_tags:
blk_mq_free_tag_set(&dev->tag_set);
+out_remove_minor:
+ idr_remove(&ubiblock_minor_idr, gd->first_minor);
out_put_disk:
put_disk(dev->gd);
out_free_dev:
@@ -463,6 +474,7 @@ static void ubiblock_cleanup(struct ubiblock *dev)
blk_cleanup_queue(dev->rq);
blk_mq_free_tag_set(&dev->tag_set);
dev_info(disk_to_dev(dev->gd), "released");
+ idr_remove(&ubiblock_minor_idr, dev->gd->first_minor);
put_disk(dev->gd);
}
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index b7f824d..22fd19c 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -83,8 +83,6 @@ static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES];
static bool fm_autoconvert;
static bool fm_debug;
#endif
-/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
-struct class *ubi_class;
/* Slab cache for wear-leveling entries */
struct kmem_cache *ubi_wl_entry_slab;
@@ -113,8 +111,17 @@ static ssize_t ubi_version_show(struct class *class,
}
/* UBI version attribute ('/<sysfs>/class/ubi/version') */
-static struct class_attribute ubi_version =
- __ATTR(version, S_IRUGO, ubi_version_show, NULL);
+static struct class_attribute ubi_class_attrs[] = {
+ __ATTR(version, S_IRUGO, ubi_version_show, NULL),
+ __ATTR_NULL
+};
+
+/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
+struct class ubi_class = {
+ .name = UBI_NAME_STR,
+ .owner = THIS_MODULE,
+ .class_attrs = ubi_class_attrs,
+};
static ssize_t dev_attribute_show(struct device *dev,
struct device_attribute *attr, char *buf);
@@ -385,6 +392,22 @@ static ssize_t dev_attribute_show(struct device *dev,
return ret;
}
+static struct attribute *ubi_dev_attrs[] = {
+ &dev_eraseblock_size.attr,
+ &dev_avail_eraseblocks.attr,
+ &dev_total_eraseblocks.attr,
+ &dev_volumes_count.attr,
+ &dev_max_ec.attr,
+ &dev_reserved_for_bad.attr,
+ &dev_bad_peb_count.attr,
+ &dev_max_vol_count.attr,
+ &dev_min_io_size.attr,
+ &dev_bgt_enabled.attr,
+ &dev_mtd_num.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(ubi_dev);
+
static void dev_release(struct device *dev)
{
struct ubi_device *ubi = container_of(dev, struct ubi_device, dev);
@@ -407,45 +430,15 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
ubi->dev.release = dev_release;
ubi->dev.devt = ubi->cdev.dev;
- ubi->dev.class = ubi_class;
+ ubi->dev.class = &ubi_class;
+ ubi->dev.groups = ubi_dev_groups;
dev_set_name(&ubi->dev, UBI_NAME_STR"%d", ubi->ubi_num);
err = device_register(&ubi->dev);
if (err)
return err;
*ref = 1;
- err = device_create_file(&ubi->dev, &dev_eraseblock_size);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_avail_eraseblocks);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_total_eraseblocks);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_volumes_count);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_max_ec);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_reserved_for_bad);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_bad_peb_count);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_max_vol_count);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_min_io_size);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_bgt_enabled);
- if (err)
- return err;
- err = device_create_file(&ubi->dev, &dev_mtd_num);
- return err;
+ return 0;
}
/**
@@ -454,17 +447,6 @@ static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
*/
static void ubi_sysfs_close(struct ubi_device *ubi)
{
- device_remove_file(&ubi->dev, &dev_mtd_num);
- device_remove_file(&ubi->dev, &dev_bgt_enabled);
- device_remove_file(&ubi->dev, &dev_min_io_size);
- device_remove_file(&ubi->dev, &dev_max_vol_count);
- device_remove_file(&ubi->dev, &dev_bad_peb_count);
- device_remove_file(&ubi->dev, &dev_reserved_for_bad);
- device_remove_file(&ubi->dev, &dev_max_ec);
- device_remove_file(&ubi->dev, &dev_volumes_count);
- device_remove_file(&ubi->dev, &dev_total_eraseblocks);
- device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
- device_remove_file(&ubi->dev, &dev_eraseblock_size);
device_unregister(&ubi->dev);
}
@@ -947,8 +929,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
*/
ubi->fm_pool.max_size = min(((int)mtd_div_by_eb(ubi->mtd->size,
ubi->mtd) / 100) * 5, UBI_FM_MAX_POOL_SIZE);
- if (ubi->fm_pool.max_size < UBI_FM_MIN_POOL_SIZE)
- ubi->fm_pool.max_size = UBI_FM_MIN_POOL_SIZE;
+ ubi->fm_pool.max_size = max(ubi->fm_pool.max_size,
+ UBI_FM_MIN_POOL_SIZE);
ubi->fm_wl_pool.max_size = ubi->fm_pool.max_size / 2;
ubi->fm_disabled = !fm_autoconvert;
@@ -1233,23 +1215,14 @@ static int __init ubi_init(void)
}
/* Create base sysfs directory and sysfs files */
- ubi_class = class_create(THIS_MODULE, UBI_NAME_STR);
- if (IS_ERR(ubi_class)) {
- err = PTR_ERR(ubi_class);
- pr_err("UBI error: cannot create UBI class");
- goto out;
- }
-
- err = class_create_file(ubi_class, &ubi_version);
- if (err) {
- pr_err("UBI error: cannot create sysfs file");
- goto out_class;
- }
+ err = class_register(&ubi_class);
+ if (err < 0)
+ return err;
err = misc_register(&ubi_ctrl_cdev);
if (err) {
pr_err("UBI error: cannot register device");
- goto out_version;
+ goto out;
}
ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
@@ -1333,11 +1306,8 @@ out_slab:
kmem_cache_destroy(ubi_wl_entry_slab);
out_dev_unreg:
misc_deregister(&ubi_ctrl_cdev);
-out_version:
- class_remove_file(ubi_class, &ubi_version);
-out_class:
- class_destroy(ubi_class);
out:
+ class_unregister(&ubi_class);
pr_err("UBI error: cannot initialize UBI, error %d", err);
return err;
}
@@ -1358,8 +1328,7 @@ static void __exit ubi_exit(void)
ubi_debugfs_exit();
kmem_cache_destroy(ubi_wl_entry_slab);
misc_deregister(&ubi_ctrl_cdev);
- class_remove_file(ubi_class, &ubi_version);
- class_destroy(ubi_class);
+ class_unregister(&ubi_class);
}
module_exit(ubi_exit);
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 02a6de2..4aa2fd8 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -88,13 +88,13 @@ size_t ubi_calc_fm_size(struct ubi_device *ubi)
{
size_t size;
- size = sizeof(struct ubi_fm_sb) + \
- sizeof(struct ubi_fm_hdr) + \
- sizeof(struct ubi_fm_scan_pool) + \
- sizeof(struct ubi_fm_scan_pool) + \
- (ubi->peb_count * sizeof(struct ubi_fm_ec)) + \
- (sizeof(struct ubi_fm_eba) + \
- (ubi->peb_count * sizeof(__be32))) + \
+ size = sizeof(struct ubi_fm_sb) +
+ sizeof(struct ubi_fm_hdr) +
+ sizeof(struct ubi_fm_scan_pool) +
+ sizeof(struct ubi_fm_scan_pool) +
+ (ubi->peb_count * sizeof(struct ubi_fm_ec)) +
+ (sizeof(struct ubi_fm_eba) +
+ (ubi->peb_count * sizeof(__be32))) +
sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES;
return roundup(size, ubi->leb_size);
}
@@ -192,8 +192,10 @@ static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id,
if (vol_id > av->vol_id)
p = &(*p)->rb_left;
- else
+ else if (vol_id < av->vol_id)
p = &(*p)->rb_right;
+ else
+ return ERR_PTR(-EINVAL);
}
av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL);
@@ -314,7 +316,7 @@ static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai,
list_add_tail(&victim->u.list, &ai->erase);
if (av->highest_lnum == be32_to_cpu(new_vh->lnum))
- av->last_data_size = \
+ av->last_data_size =
be32_to_cpu(new_vh->data_size);
dbg_bld("vol %i: AEB %i's PEB %i is the newer",
@@ -601,7 +603,7 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb;
struct ubi_fm_sb *fmsb;
struct ubi_fm_hdr *fmhdr;
- struct ubi_fm_scan_pool *fmpl1, *fmpl2;
+ struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
struct ubi_fm_ec *fmec;
struct ubi_fm_volhdr *fmvhdr;
struct ubi_fm_eba *fm_eba;
@@ -631,30 +633,30 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
goto fail_bad;
}
- fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
- fm_pos += sizeof(*fmpl1);
+ fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl);
if (fm_pos >= fm_size)
goto fail_bad;
- if (be32_to_cpu(fmpl1->magic) != UBI_FM_POOL_MAGIC) {
+ if (be32_to_cpu(fmpl->magic) != UBI_FM_POOL_MAGIC) {
ubi_err(ubi, "bad fastmap pool magic: 0x%x, expected: 0x%x",
- be32_to_cpu(fmpl1->magic), UBI_FM_POOL_MAGIC);
+ be32_to_cpu(fmpl->magic), UBI_FM_POOL_MAGIC);
goto fail_bad;
}
- fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
- fm_pos += sizeof(*fmpl2);
+ fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl_wl);
if (fm_pos >= fm_size)
goto fail_bad;
- if (be32_to_cpu(fmpl2->magic) != UBI_FM_POOL_MAGIC) {
- ubi_err(ubi, "bad fastmap pool magic: 0x%x, expected: 0x%x",
- be32_to_cpu(fmpl2->magic), UBI_FM_POOL_MAGIC);
+ if (be32_to_cpu(fmpl_wl->magic) != UBI_FM_POOL_MAGIC) {
+ ubi_err(ubi, "bad fastmap WL pool magic: 0x%x, expected: 0x%x",
+ be32_to_cpu(fmpl_wl->magic), UBI_FM_POOL_MAGIC);
goto fail_bad;
}
- pool_size = be16_to_cpu(fmpl1->size);
- wl_pool_size = be16_to_cpu(fmpl2->size);
- fm->max_pool_size = be16_to_cpu(fmpl1->max_size);
- fm->max_wl_pool_size = be16_to_cpu(fmpl2->max_size);
+ pool_size = be16_to_cpu(fmpl->size);
+ wl_pool_size = be16_to_cpu(fmpl_wl->size);
+ fm->max_pool_size = be16_to_cpu(fmpl->max_size);
+ fm->max_wl_pool_size = be16_to_cpu(fmpl_wl->max_size);
if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) {
ubi_err(ubi, "bad pool size: %i", pool_size);
@@ -748,6 +750,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
if (!av)
goto fail_bad;
+ if (PTR_ERR(av) == -EINVAL) {
+ ubi_err(ubi, "volume (ID %i) already exists",
+ fmvhdr->vol_id);
+ goto fail_bad;
+ }
ai->vols_found++;
if (ai->highest_vol_id < be32_to_cpu(fmvhdr->vol_id))
@@ -796,11 +803,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
}
}
- ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum, &free);
+ ret = scan_pool(ubi, ai, fmpl->pebs, pool_size, &max_sqnum, &free);
if (ret)
goto fail;
- ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum, &free);
+ ret = scan_pool(ubi, ai, fmpl_wl->pebs, wl_pool_size, &max_sqnum, &free);
if (ret)
goto fail;
@@ -1083,7 +1090,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
void *fm_raw;
struct ubi_fm_sb *fmsb;
struct ubi_fm_hdr *fmh;
- struct ubi_fm_scan_pool *fmpl1, *fmpl2;
+ struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
struct ubi_fm_ec *fec;
struct ubi_fm_volhdr *fvh;
struct ubi_fm_eba *feba;
@@ -1141,25 +1148,25 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
erase_peb_count = 0;
vol_count = 0;
- fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
- fm_pos += sizeof(*fmpl1);
- fmpl1->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
- fmpl1->size = cpu_to_be16(ubi->fm_pool.size);
- fmpl1->max_size = cpu_to_be16(ubi->fm_pool.max_size);
+ fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl);
+ fmpl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
+ fmpl->size = cpu_to_be16(ubi->fm_pool.size);
+ fmpl->max_size = cpu_to_be16(ubi->fm_pool.max_size);
for (i = 0; i < ubi->fm_pool.size; i++) {
- fmpl1->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]);
+ fmpl->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]);
set_seen(ubi, ubi->fm_pool.pebs[i], seen_pebs);
}
- fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
- fm_pos += sizeof(*fmpl2);
- fmpl2->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
- fmpl2->size = cpu_to_be16(ubi->fm_wl_pool.size);
- fmpl2->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size);
+ fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl_wl);
+ fmpl_wl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
+ fmpl_wl->size = cpu_to_be16(ubi->fm_wl_pool.size);
+ fmpl_wl->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size);
for (i = 0; i < ubi->fm_wl_pool.size; i++) {
- fmpl2->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]);
+ fmpl_wl->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]);
set_seen(ubi, ubi->fm_wl_pool.pebs[i], seen_pebs);
}
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index c998212..2974b67 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -775,7 +775,7 @@ extern struct kmem_cache *ubi_wl_entry_slab;
extern const struct file_operations ubi_ctrl_cdev_operations;
extern const struct file_operations ubi_cdev_operations;
extern const struct file_operations ubi_vol_cdev_operations;
-extern struct class *ubi_class;
+extern struct class ubi_class;
extern struct mutex ubi_devices_mutex;
extern struct blocking_notifier_head ubi_notifiers;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index ff4d978..1ae17bb 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -120,6 +120,19 @@ static ssize_t vol_attribute_show(struct device *dev,
return ret;
}
+static struct attribute *volume_dev_attrs[] = {
+ &attr_vol_reserved_ebs.attr,
+ &attr_vol_type.attr,
+ &attr_vol_name.attr,
+ &attr_vol_corrupted.attr,
+ &attr_vol_alignment.attr,
+ &attr_vol_usable_eb_size.attr,
+ &attr_vol_data_bytes.attr,
+ &attr_vol_upd_marker.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(volume_dev);
+
/* Release method for volume devices */
static void vol_release(struct device *dev)
{
@@ -130,64 +143,6 @@ static void vol_release(struct device *dev)
}
/**
- * volume_sysfs_init - initialize sysfs for new volume.
- * @ubi: UBI device description object
- * @vol: volume description object
- *
- * This function returns zero in case of success and a negative error code in
- * case of failure.
- *
- * Note, this function does not free allocated resources in case of failure -
- * the caller does it. This is because this would cause release() here and the
- * caller would oops.
- */
-static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol)
-{
- int err;
-
- err = device_create_file(&vol->dev, &attr_vol_reserved_ebs);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_type);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_name);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_corrupted);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_alignment);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_usable_eb_size);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_data_bytes);
- if (err)
- return err;
- err = device_create_file(&vol->dev, &attr_vol_upd_marker);
- return err;
-}
-
-/**
- * volume_sysfs_close - close sysfs for a volume.
- * @vol: volume description object
- */
-static void volume_sysfs_close(struct ubi_volume *vol)
-{
- device_remove_file(&vol->dev, &attr_vol_upd_marker);
- device_remove_file(&vol->dev, &attr_vol_data_bytes);
- device_remove_file(&vol->dev, &attr_vol_usable_eb_size);
- device_remove_file(&vol->dev, &attr_vol_alignment);
- device_remove_file(&vol->dev, &attr_vol_corrupted);
- device_remove_file(&vol->dev, &attr_vol_name);
- device_remove_file(&vol->dev, &attr_vol_type);
- device_remove_file(&vol->dev, &attr_vol_reserved_ebs);
- device_unregister(&vol->dev);
-}
-
-/**
* ubi_create_volume - create volume.
* @ubi: UBI device description object
* @req: volume creation request
@@ -253,8 +208,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
/* Calculate how many eraseblocks are requested */
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
- vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1,
- vol->usable_leb_size);
+ vol->reserved_pebs = div_u64(req->bytes + vol->usable_leb_size - 1,
+ vol->usable_leb_size);
/* Reserve physical eraseblocks */
if (vol->reserved_pebs > ubi->avail_pebs) {
@@ -323,7 +278,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
vol->dev.release = vol_release;
vol->dev.parent = &ubi->dev;
vol->dev.devt = dev;
- vol->dev.class = ubi_class;
+ vol->dev.class = &ubi_class;
+ vol->dev.groups = volume_dev_groups;
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
err = device_register(&vol->dev);
@@ -332,10 +288,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
goto out_cdev;
}
- err = volume_sysfs_init(ubi, vol);
- if (err)
- goto out_sysfs;
-
/* Fill volume table record */
memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs);
@@ -372,7 +324,7 @@ out_sysfs:
*/
do_free = 0;
get_device(&vol->dev);
- volume_sysfs_close(vol);
+ device_unregister(&vol->dev);
out_cdev:
cdev_del(&vol->cdev);
out_mapping:
@@ -440,7 +392,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
}
cdev_del(&vol->cdev);
- volume_sysfs_close(vol);
+ device_unregister(&vol->dev);
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= reserved_pebs;
@@ -653,19 +605,13 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
vol->dev.release = vol_release;
vol->dev.parent = &ubi->dev;
vol->dev.devt = dev;
- vol->dev.class = ubi_class;
+ vol->dev.class = &ubi_class;
+ vol->dev.groups = volume_dev_groups;
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
err = device_register(&vol->dev);
if (err)
goto out_cdev;
- err = volume_sysfs_init(ubi, vol);
- if (err) {
- cdev_del(&vol->cdev);
- volume_sysfs_close(vol);
- return err;
- }
-
self_check_volumes(ubi);
return err;
@@ -688,7 +634,7 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
ubi->volumes[vol->vol_id] = NULL;
cdev_del(&vol->cdev);
- volume_sysfs_close(vol);
+ device_unregister(&vol->dev);
}
/**
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index 68c9c5ea..80bdd5b 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -70,6 +70,26 @@ static void self_vtbl_check(const struct ubi_device *ubi);
static struct ubi_vtbl_record empty_vtbl_record;
/**
+ * ubi_update_layout_vol - helper for updatting layout volumes on flash
+ * @ubi: UBI device description object
+ */
+static int ubi_update_layout_vol(struct ubi_device *ubi)
+{
+ struct ubi_volume *layout_vol;
+ int i, err;
+
+ layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
+ for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+ err = ubi_eba_atomic_leb_change(ubi, layout_vol, i, ubi->vtbl,
+ ubi->vtbl_size);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
* ubi_change_vtbl_record - change volume table record.
* @ubi: UBI device description object
* @idx: table index to change
@@ -83,12 +103,10 @@ static struct ubi_vtbl_record empty_vtbl_record;
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
struct ubi_vtbl_record *vtbl_rec)
{
- int i, err;
+ int err;
uint32_t crc;
- struct ubi_volume *layout_vol;
ubi_assert(idx >= 0 && idx < ubi->vtbl_slots);
- layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
if (!vtbl_rec)
vtbl_rec = &empty_vtbl_record;
@@ -98,15 +116,10 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
}
memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record));
- for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
- err = ubi_eba_atomic_leb_change(ubi, layout_vol, i, ubi->vtbl,
- ubi->vtbl_size);
- if (err)
- return err;
- }
+ err = ubi_update_layout_vol(ubi);
self_vtbl_check(ubi);
- return 0;
+ return err ? err : 0;
}
/**
@@ -121,9 +134,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
struct list_head *rename_list)
{
- int i, err;
struct ubi_rename_entry *re;
- struct ubi_volume *layout_vol;
list_for_each_entry(re, rename_list, list) {
uint32_t crc;
@@ -145,15 +156,7 @@ int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
vtbl_rec->crc = cpu_to_be32(crc);
}
- layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
- for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
- err = ubi_eba_atomic_leb_change(ubi, layout_vol, i, ubi->vtbl,
- ubi->vtbl_size);
- if (err)
- return err;
- }
-
- return 0;
+ return ubi_update_layout_vol(ubi);
}
/**
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 16214d3..275d9fb 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1581,7 +1581,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
dbg_wl("found %i PEBs", found_pebs);
if (ubi->fm) {
- ubi_assert(ubi->good_peb_count == \
+ ubi_assert(ubi->good_peb_count ==
found_pebs + ubi->fm->used_blocks);
for (i = 0; i < ubi->fm->used_blocks; i++) {
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 594c918..1ef02da 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -372,7 +372,8 @@ static int acerhdf_bind(struct thermal_zone_device *thermal,
return 0;
if (thermal_zone_bind_cooling_device(thermal, 0, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT)) {
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT)) {
pr_err("error binding cooling dev\n");
return -EINVAL;
}
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index 9094163..5e947a8 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -78,11 +78,6 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
if (ret != 0)
return ret;
- ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
- ARIZONA_SUBSYS_MAX_FREQ, val);
- if (ret != 0)
- return ret;
-
if (val)
return 0;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5e963df..db2fe4a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -12,7 +12,7 @@ menuconfig RTC_CLASS
select RTC_LIB
help
Generic RTC class support. If you say yes here, you will
- be allowed to plug one or more RTCs to your system. You will
+ be allowed to plug one or more RTCs to your system. You will
probably want to enable one or more of the interfaces below.
if RTC_CLASS
@@ -25,17 +25,9 @@ config RTC_HCTOSYS
the value read from a specified RTC device. This is useful to avoid
unnecessary fsck runs at boot time, and to network better.
-config RTC_SYSTOHC
- bool "Set the RTC time based on NTP synchronization"
- default y
- help
- If you say yes here, the system time (wall clock) will be stored
- in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11
- minutes if userspace reports synchronized NTP status.
-
config RTC_HCTOSYS_DEVICE
string "RTC used to set the system time"
- depends on RTC_HCTOSYS = y || RTC_SYSTOHC = y
+ depends on RTC_HCTOSYS
default "rtc0"
help
The RTC device that will be used to (re)initialize the system
@@ -56,6 +48,25 @@ config RTC_HCTOSYS_DEVICE
sleep states. Do not specify an RTC here unless it stays powered
during all this system's supported sleep states.
+config RTC_SYSTOHC
+ bool "Set the RTC time based on NTP synchronization"
+ default y
+ help
+ If you say yes here, the system time (wall clock) will be stored
+ in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11
+ minutes if userspace reports synchronized NTP status.
+
+config RTC_SYSTOHC_DEVICE
+ string "RTC used to synchronize NTP adjustment"
+ depends on RTC_SYSTOHC
+ default RTC_HCTOSYS_DEVICE if RTC_HCTOSYS
+ default "rtc0"
+ help
+ The RTC device used for NTP synchronization. The main difference
+ between RTC_HCTOSYS_DEVICE and RTC_SYSTOHC_DEVICE is that this
+ one can sleep when setting time, because it runs in the workqueue
+ context.
+
config RTC_DEBUG
bool "RTC debug support"
help
@@ -135,7 +146,7 @@ if I2C
config RTC_DRV_88PM860X
tristate "Marvell 88PM860x"
- depends on I2C && MFD_88PM860X
+ depends on MFD_88PM860X
help
If you say yes here you get support for RTC function in Marvell
88PM860x chips.
@@ -145,7 +156,7 @@ config RTC_DRV_88PM860X
config RTC_DRV_88PM80X
tristate "Marvell 88PM80x"
- depends on I2C && MFD_88PM800
+ depends on MFD_88PM800
help
If you say yes here you get support for RTC function in Marvell
88PM80x chips.
@@ -154,10 +165,9 @@ config RTC_DRV_88PM80X
will be called rtc-88pm80x.
config RTC_DRV_ABB5ZES3
- depends on I2C
- select REGMAP_I2C
- tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3"
- help
+ select REGMAP_I2C
+ tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3"
+ help
If you say yes here you get support for the Abracon
AB-RTCMC-32.768kHz-B5ZE-S3 I2C RTC chip.
@@ -204,7 +214,6 @@ config RTC_DRV_DS1307
config RTC_DRV_DS1374
tristate "Dallas/Maxim DS1374"
- depends on I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1374 real-time clock chips. If an interrupt is associated
@@ -232,7 +241,6 @@ config RTC_DRV_DS1672
config RTC_DRV_DS3232
tristate "Dallas/Maxim DS3232"
- depends on I2C
help
If you say yes here you get support for Dallas Semiconductor
DS3232 real-time clock chips. If an interrupt is associated
@@ -243,7 +251,7 @@ config RTC_DRV_DS3232
config RTC_DRV_HYM8563
tristate "Haoyu Microelectronics HYM8563"
- depends on I2C && OF
+ depends on OF
help
Say Y to enable support for the HYM8563 I2C RTC chip. Apart
from the usual rtc functions it provides a clock output of
@@ -365,10 +373,9 @@ config RTC_DRV_ISL12022
will be called rtc-isl12022.
config RTC_DRV_ISL12057
- depends on I2C
- select REGMAP_I2C
- tristate "Intersil ISL12057"
- help
+ select REGMAP_I2C
+ tristate "Intersil ISL12057"
+ help
If you say yes here you get support for the Intersil ISL12057
I2C RTC chip.
@@ -603,13 +610,13 @@ comment "SPI RTC drivers"
if SPI_MASTER
config RTC_DRV_M41T93
- tristate "ST M41T93"
- help
- If you say yes here you will get support for the
- ST M41T93 SPI RTC chip.
+ tristate "ST M41T93"
+ help
+ If you say yes here you will get support for the
+ ST M41T93 SPI RTC chip.
- This driver can also be built as a module. If so, the module
- will be called rtc-m41t93.
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t93.
config RTC_DRV_M41T94
tristate "ST M41T94"
@@ -1200,7 +1207,7 @@ config RTC_DRV_SH
Say Y here to enable support for the on-chip RTC found in
most SuperH processors.
- To compile this driver as a module, choose M here: the
+ To compile this driver as a module, choose M here: the
module will be called rtc-sh.
config RTC_DRV_VR41XX
@@ -1299,14 +1306,14 @@ config RTC_DRV_GENERIC
just say Y.
config RTC_DRV_PXA
- tristate "PXA27x/PXA3xx"
- depends on ARCH_PXA
- help
- If you say Y here you will get access to the real time clock
- built into your PXA27x or PXA3xx CPU.
+ tristate "PXA27x/PXA3xx"
+ depends on ARCH_PXA
+ help
+ If you say Y here you will get access to the real time clock
+ built into your PXA27x or PXA3xx CPU.
- This RTC driver uses PXA RTC registers available since pxa27x
- series (RDxR, RYxR) instead of legacy RCNR, RTAR.
+ This RTC driver uses PXA RTC registers available since pxa27x
+ series (RDxR, RYxR) instead of legacy RCNR, RTAR.
config RTC_DRV_VT8500
tristate "VIA/WonderMedia 85xx SoC RTC"
@@ -1372,6 +1379,17 @@ config RTC_DRV_ARMADA38X
This driver can also be built as a module. If so, the module
will be called armada38x-rtc.
+config RTC_DRV_GEMINI
+ tristate "Gemini SoC RTC"
+ depends on ARCH_GEMINI || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ If you say Y here you will get support for the
+ RTC found on Gemini SoC's.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-gemini.
+
config RTC_DRV_PS3
tristate "PS3 RTC"
depends on PPC_PS3
@@ -1396,6 +1414,7 @@ config RTC_DRV_COH901331
config RTC_DRV_STMP
tristate "Freescale STMP3xxx/i.MX23/i.MX28 RTC"
depends on ARCH_MXS
+ select STMP_DEVICE
help
If you say yes here you will get support for the onboard
STMP3xxx/i.MX23/i.MX28 RTC.
@@ -1541,9 +1560,20 @@ config RTC_DRV_MOXART
This driver can also be built as a module. If so, the module
will be called rtc-moxart
+config RTC_DRV_MT6397
+ tristate "Mediatek Real Time Clock driver"
+ depends on MFD_MT6397 || COMPILE_TEST
+ help
+ This selects the Mediatek(R) RTC driver. RTC is part of Mediatek
+ MT6397 PMIC. You should enable MT6397 PMIC MFD before select
+ Mediatek(R) RTC driver.
+
+ If you want to use Mediatek(R) RTC interface, select Y or M here.
+
config RTC_DRV_XGENE
tristate "APM X-Gene RTC"
depends on HAS_IOMEM
+ depends on ARCH_XGENE || COMPILE_TEST
help
If you say yes here you get support for the APM X-Gene SoC real time
clock.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index ebe2c08..1b09a62 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -14,14 +14,14 @@ ifdef CONFIG_RTC_DRV_EFI
rtc-core-y += rtc-efi-platform.o
endif
-rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
-rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
-rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
+rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
+rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
+rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
# Keep the list ordered.
-obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
+obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
@@ -43,7 +43,6 @@ obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
-obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
@@ -58,20 +57,21 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1685_FAMILY) += rtc-ds1685.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
-obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o
+obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o
obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
-obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_ISL12057) += rtc-isl12057.o
+obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o
obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
@@ -82,32 +82,35 @@ obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
-obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
+obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
+obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o
obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
-obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
-obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
-obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
-obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o
+obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o
-obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
+obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
+obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
+obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
-obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
+obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
+obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
+obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
+obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
-obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
-obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
-obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
@@ -130,21 +133,23 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
+obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
-obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o
+obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
@@ -153,6 +158,3 @@ obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
-obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
-obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
-obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 166fc60..11b6390 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -91,51 +91,6 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
}
EXPORT_SYMBOL_GPL(rtc_set_time);
-int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
-{
- int err;
-
- err = mutex_lock_interruptible(&rtc->ops_lock);
- if (err)
- return err;
-
- if (!rtc->ops)
- err = -ENODEV;
- else if (rtc->ops->set_mmss64)
- err = rtc->ops->set_mmss64(rtc->dev.parent, secs);
- else if (rtc->ops->set_mmss)
- err = rtc->ops->set_mmss(rtc->dev.parent, secs);
- else if (rtc->ops->read_time && rtc->ops->set_time) {
- struct rtc_time new, old;
-
- err = rtc->ops->read_time(rtc->dev.parent, &old);
- if (err == 0) {
- rtc_time64_to_tm(secs, &new);
-
- /*
- * avoid writing when we're going to change the day of
- * the month. We will retry in the next minute. This
- * basically means that if the RTC must not drift
- * by more than 1 minute in 11 minutes.
- */
- if (!((old.tm_hour == 23 && old.tm_min == 59) ||
- (new.tm_hour == 23 && new.tm_min == 59)))
- err = rtc->ops->set_time(rtc->dev.parent,
- &new);
- }
- } else {
- err = -EINVAL;
- }
-
- pm_stay_awake(rtc->dev.parent);
- mutex_unlock(&rtc->ops_lock);
- /* A timer might have just expired */
- schedule_work(&rtc->irqwork);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(rtc_set_mmss);
-
static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
@@ -976,14 +931,12 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
*
* Kernel interface to cancel an rtc_timer
*/
-int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
+void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
{
- int ret = 0;
mutex_lock(&rtc->ops_lock);
if (timer->enabled)
rtc_timer_remove(rtc, timer);
mutex_unlock(&rtc->ops_lock);
- return ret;
}
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 6856f0a..133d2e2 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -442,7 +442,7 @@ static const struct rtc_class_ops ab8540_rtc_ops = {
.alarm_irq_enable = ab8500_rtc_irq_enable,
};
-static struct platform_device_id ab85xx_rtc_ids[] = {
+static const struct platform_device_id ab85xx_rtc_ids[] = {
{ "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, },
{ "ab8540-rtc", (kernel_ulong_t)&ab8540_rtc_ops, },
};
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index d618d6c..83ac233 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -282,6 +282,6 @@ static struct platform_driver at32_rtc_driver = {
module_platform_driver_probe(at32_rtc_driver, at32_rtc_probe);
-MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c
index d16f550..12dbd70 100644
--- a/drivers/rtc/rtc-ds1216.c
+++ b/drivers/rtc/rtc-ds1216.c
@@ -144,15 +144,13 @@ static int __init ds1216_rtc_probe(struct platform_device *pdev)
struct ds1216_priv *priv;
u8 dummy[8];
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->ioaddr))
return PTR_ERR(priv->ioaddr);
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
index 2fe537f..8247a29 100644
--- a/drivers/rtc/rtc-ds1286.c
+++ b/drivers/rtc/rtc-ds1286.c
@@ -332,13 +332,11 @@ static int ds1286_probe(struct platform_device *pdev)
struct resource *res;
struct ds1286_priv *priv;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
priv = devm_kzalloc(&pdev->dev, sizeof(struct ds1286_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->rtcregs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->rtcregs))
return PTR_ERR(priv->rtcregs);
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4ffabb3..6e76de1 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -742,17 +742,17 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
regs[6] &= ~MCP794XX_BIT_ALMX_IF;
/* Set alarm match: second, minute, hour, day, date, month. */
regs[6] |= MCP794XX_MSK_ALMX_MATCH;
-
- if (t->enabled)
- regs[0] |= MCP794XX_BIT_ALM0_EN;
- else
- regs[0] &= ~MCP794XX_BIT_ALM0_EN;
+ /* Disable interrupt. We will not enable until completely programmed */
+ regs[0] &= ~MCP794XX_BIT_ALM0_EN;
ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
if (ret < 0)
return ret;
- return 0;
+ if (!t->enabled)
+ return 0;
+ regs[0] |= MCP794XX_BIT_ALM0_EN;
+ return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, regs[0]);
}
static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index a4888db..92b1cbf 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -198,6 +198,7 @@ static struct i2c_device_id ds1672_id[] = {
{ "ds1672", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, ds1672_id);
static struct i2c_driver ds1672_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index cb989cd..3806961 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
*
- * Author: dann frazier <dannf@hp.com>
+ * Author: dann frazier <dannf@dannf.org>
* Based on efirtc.c by Stephane Eranian
*
* This program is free software; you can redistribute it and/or modify it
@@ -24,10 +24,6 @@
#include <linux/efi.h>
#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
-/*
- * EFI Epoch is 1/1/1998
- */
-#define EFI_RTC_EPOCH 1998
/*
* returns day of the year [0-365]
@@ -38,31 +34,24 @@ compute_yday(efi_time_t *eft)
/* efi_time_t.month is in the [1-12] so, we need -1 */
return rtc_year_days(eft->day, eft->month - 1, eft->year);
}
+
/*
* returns day of the week [0-6] 0=Sunday
- *
- * Don't try to provide a year that's before 1998, please !
*/
static int
-compute_wday(efi_time_t *eft)
+compute_wday(efi_time_t *eft, int yday)
{
- int y;
- int ndays = 0;
-
- if (eft->year < EFI_RTC_EPOCH) {
- pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n");
- return -1;
- }
-
- for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(eft);
+ int ndays = eft->year * (365 % 7)
+ + (eft->year - 1) / 4
+ - (eft->year - 1) / 100
+ + (eft->year - 1) / 400
+ + yday;
/*
- * 4=1/1/1998 was a Thursday
+ * 1/1/0000 may or may not have been a Sunday (if it ever existed at
+ * all) but assuming it was makes this calculation work correctly.
*/
- return (ndays + 4) % 7;
+ return ndays % 7;
}
static void
@@ -103,16 +92,16 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
if (!eft->month || eft->month > 12)
return false;
wtime->tm_mon = eft->month - 1;
- wtime->tm_year = eft->year - 1900;
- /* day of the week [0-6], Sunday=0 */
- wtime->tm_wday = compute_wday(eft);
- if (wtime->tm_wday < 0)
+ if (eft->year < 1900 || eft->year > 9999)
return false;
+ wtime->tm_year = eft->year - 1900;
/* day in the year [1-365]*/
wtime->tm_yday = compute_yday(eft);
+ /* day of the week [0-6], Sunday=0 */
+ wtime->tm_wday = compute_wday(eft, wtime->tm_yday);
switch (eft->daylight & EFI_ISDST) {
case EFI_ISDST:
@@ -233,7 +222,7 @@ static struct platform_driver efi_rtc_driver = {
module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
MODULE_ALIAS("platform:rtc-efi");
-MODULE_AUTHOR("dann frazier <dannf@hp.com>");
+MODULE_AUTHOR("dann frazier <dannf@dannf.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EFI RTC driver");
MODULE_ALIAS("platform:rtc-efi");
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index de325d6..a1628ad 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -45,7 +45,7 @@ static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
unsigned long comp;
- comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
+ comp = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
if (preload)
*preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK)
@@ -63,7 +63,7 @@ static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
unsigned long time;
- time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
+ time = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
rtc_time_to_tm(time, tm);
return 0;
@@ -73,7 +73,7 @@ static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
{
struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
- __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
+ writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
return 0;
}
diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-gemini.c
new file mode 100644
index 0000000..35f4486
--- /dev/null
+++ b/drivers/rtc/rtc-gemini.c
@@ -0,0 +1,175 @@
+/*
+ * Gemini OnChip RTC
+ *
+ * Copyright (C) 2009 Janos Laube <janos.dev@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.
+ *
+ * Original code for older kernel 2.6.15 are from Stormlinksemi
+ * first update from Janos Laube for > 2.6.29 kernels
+ *
+ * checkpatch fixes and usage of rtc-lib code
+ * Hans Ulli Kroll <ulli.kroll@googlemail.com>
+ */
+
+#include <linux/rtc.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define DRV_NAME "rtc-gemini"
+#define DRV_VERSION "0.2"
+
+MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>");
+MODULE_DESCRIPTION("RTC driver for Gemini SoC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+struct gemini_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *rtc_base;
+ int rtc_irq;
+};
+
+enum gemini_rtc_offsets {
+ GEMINI_RTC_SECOND = 0x00,
+ GEMINI_RTC_MINUTE = 0x04,
+ GEMINI_RTC_HOUR = 0x08,
+ GEMINI_RTC_DAYS = 0x0C,
+ GEMINI_RTC_ALARM_SECOND = 0x10,
+ GEMINI_RTC_ALARM_MINUTE = 0x14,
+ GEMINI_RTC_ALARM_HOUR = 0x18,
+ GEMINI_RTC_RECORD = 0x1C,
+ GEMINI_RTC_CR = 0x20
+};
+
+static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
+{
+ return IRQ_HANDLED;
+}
+
+/*
+ * Looks like the RTC in the Gemini SoC is (totaly) broken
+ * We can't read/write directly the time from RTC registers.
+ * We must do some "offset" calculation to get the real time
+ *
+ * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does
+ * the same thing, without the rtc-lib.c calls.
+ */
+
+static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct gemini_rtc *rtc = dev_get_drvdata(dev);
+
+ unsigned int days, hour, min, sec;
+ unsigned long offset, time;
+
+ sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
+ min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
+ hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
+ days = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
+ offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD);
+
+ time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
+
+ rtc_time_to_tm(time, tm);
+
+ return 0;
+}
+
+static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct gemini_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int sec, min, hour, day;
+ unsigned long offset, time;
+
+ if (tm->tm_year >= 2148) /* EPOCH Year + 179 */
+ return -EINVAL;
+
+ rtc_tm_to_time(tm, &time);
+
+ sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
+ min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
+ hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
+ day = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
+
+ offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
+
+ writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD);
+ writel(0x01, rtc->rtc_base + GEMINI_RTC_CR);
+
+ return 0;
+}
+
+static struct rtc_class_ops gemini_rtc_ops = {
+ .read_time = gemini_rtc_read_time,
+ .set_time = gemini_rtc_set_time,
+};
+
+static int gemini_rtc_probe(struct platform_device *pdev)
+{
+ struct gemini_rtc *rtc;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+ platform_set_drvdata(pdev, rtc);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res)
+ return -ENODEV;
+
+ rtc->rtc_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ rtc->rtc_base = devm_ioremap(dev, res->start,
+ resource_size(res));
+
+ ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt,
+ IRQF_SHARED, pdev->name, dev);
+ if (unlikely(ret))
+ return ret;
+
+ rtc->rtc_dev = rtc_device_register(pdev->name, dev,
+ &gemini_rtc_ops, THIS_MODULE);
+ if (likely(IS_ERR(rtc->rtc_dev)))
+ return PTR_ERR(rtc->rtc_dev);
+
+ return 0;
+}
+
+static int gemini_rtc_remove(struct platform_device *pdev)
+{
+ struct gemini_rtc *rtc = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(rtc->rtc_dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gemini_rtc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = gemini_rtc_probe,
+ .remove = gemini_rtc_remove,
+};
+
+module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);
diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c
index af4f85a..c398f74 100644
--- a/drivers/rtc/rtc-hid-sensor-time.c
+++ b/drivers/rtc/rtc-hid-sensor-time.c
@@ -318,7 +318,7 @@ static int hid_time_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_time_ids[] = {
+static const struct platform_device_id hid_time_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-2000a0",
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index 0f710e9..e9da795 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -548,14 +548,16 @@ static int hym8563_probe(struct i2c_client *client,
return ret;
}
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, hym8563_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- client->name, hym8563);
- if (ret < 0) {
- dev_err(&client->dev, "irq %d request failed, %d\n",
- client->irq, ret);
- return ret;
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, hym8563_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, hym8563);
+ if (ret < 0) {
+ dev_err(&client->dev, "irq %d request failed, %d\n",
+ client->irq, ret);
+ return ret;
+ }
}
/* check state of calendar information */
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index c666eab..7bffd7f 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -129,6 +129,324 @@ struct imxdi_dev {
struct work_struct work;
};
+/* Some background:
+ *
+ * The DryIce unit is a complex security/tamper monitor device. To be able do
+ * its job in a useful manner it runs a bigger statemachine to bring it into
+ * security/tamper failure state and once again to bring it out of this state.
+ *
+ * This unit can be in one of three states:
+ *
+ * - "NON-VALID STATE"
+ * always after the battery power was removed
+ * - "FAILURE STATE"
+ * if one of the enabled security events has happened
+ * - "VALID STATE"
+ * if the unit works as expected
+ *
+ * Everything stops when the unit enters the failure state including the RTC
+ * counter (to be able to detect the time the security event happened).
+ *
+ * The following events (when enabled) let the DryIce unit enter the failure
+ * state:
+ *
+ * - wire-mesh-tamper detect
+ * - external tamper B detect
+ * - external tamper A detect
+ * - temperature tamper detect
+ * - clock tamper detect
+ * - voltage tamper detect
+ * - RTC counter overflow
+ * - monotonic counter overflow
+ * - external boot
+ *
+ * If we find the DryIce unit in "FAILURE STATE" and the TDCHL cleared, we
+ * can only detect this state. In this case the unit is completely locked and
+ * must force a second "SYSTEM POR" to bring the DryIce into the
+ * "NON-VALID STATE" + "FAILURE STATE" where a recovery is possible.
+ * If the TDCHL is set in the "FAILURE STATE" we are out of luck. In this case
+ * a battery power cycle is required.
+ *
+ * In the "NON-VALID STATE" + "FAILURE STATE" we can clear the "FAILURE STATE"
+ * and recover the DryIce unit. By clearing the "NON-VALID STATE" as the last
+ * task, we bring back this unit into life.
+ */
+
+/*
+ * Do a write into the unit without interrupt support.
+ * We do not need to check the WEF here, because the only reason this kind of
+ * write error can happen is if we write to the unit twice within the 122 us
+ * interval. This cannot happen, since we are using this function only while
+ * setting up the unit.
+ */
+static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val,
+ unsigned reg)
+{
+ /* do the register write */
+ writel(val, imxdi->ioaddr + reg);
+
+ /*
+ * now it takes four 32,768 kHz clock cycles to take
+ * the change into effect = 122 us
+ */
+ usleep_range(130, 200);
+}
+
+static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dtcr;
+
+ dtcr = readl(imxdi->ioaddr + DTCR);
+
+ dev_emerg(&imxdi->pdev->dev, "DryIce tamper event detected\n");
+ /* the following flags force a transition into the "FAILURE STATE" */
+ if (dsr & DSR_VTD)
+ dev_emerg(&imxdi->pdev->dev, "%sVoltage Tamper Event\n",
+ dtcr & DTCR_VTE ? "" : "Spurious ");
+
+ if (dsr & DSR_CTD)
+ dev_emerg(&imxdi->pdev->dev, "%s32768 Hz Clock Tamper Event\n",
+ dtcr & DTCR_CTE ? "" : "Spurious ");
+
+ if (dsr & DSR_TTD)
+ dev_emerg(&imxdi->pdev->dev, "%sTemperature Tamper Event\n",
+ dtcr & DTCR_TTE ? "" : "Spurious ");
+
+ if (dsr & DSR_SAD)
+ dev_emerg(&imxdi->pdev->dev,
+ "%sSecure Controller Alarm Event\n",
+ dtcr & DTCR_SAIE ? "" : "Spurious ");
+
+ if (dsr & DSR_EBD)
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Boot Tamper Event\n",
+ dtcr & DTCR_EBE ? "" : "Spurious ");
+
+ if (dsr & DSR_ETAD)
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper A Event\n",
+ dtcr & DTCR_ETAE ? "" : "Spurious ");
+
+ if (dsr & DSR_ETBD)
+ dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper B Event\n",
+ dtcr & DTCR_ETBE ? "" : "Spurious ");
+
+ if (dsr & DSR_WTD)
+ dev_emerg(&imxdi->pdev->dev, "%sWire-mesh Tamper Event\n",
+ dtcr & DTCR_WTE ? "" : "Spurious ");
+
+ if (dsr & DSR_MCO)
+ dev_emerg(&imxdi->pdev->dev,
+ "%sMonotonic-counter Overflow Event\n",
+ dtcr & DTCR_MOE ? "" : "Spurious ");
+
+ if (dsr & DSR_TCO)
+ dev_emerg(&imxdi->pdev->dev, "%sTimer-counter Overflow Event\n",
+ dtcr & DTCR_TOE ? "" : "Spurious ");
+}
+
+static void di_what_is_to_be_done(struct imxdi_dev *imxdi,
+ const char *power_supply)
+{
+ dev_emerg(&imxdi->pdev->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n",
+ power_supply);
+}
+
+static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr;
+
+ dev_dbg(&imxdi->pdev->dev, "DSR register reports: %08X\n", dsr);
+
+ /* report the cause */
+ di_report_tamper_info(imxdi, dsr);
+
+ dcr = readl(imxdi->ioaddr + DCR);
+
+ if (dcr & DCR_FSHL) {
+ /* we are out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ /*
+ * with the next SYSTEM POR we will transit from the "FAILURE STATE"
+ * into the "NON-VALID STATE" + "FAILURE STATE"
+ */
+ di_what_is_to_be_done(imxdi, "main");
+
+ return -ENODEV;
+}
+
+static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ /* initialize alarm */
+ di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR);
+ di_write_busy_wait(imxdi, 0, DCALR);
+
+ /* clear alarm flag */
+ if (dsr & DSR_CAF)
+ di_write_busy_wait(imxdi, DSR_CAF, DSR);
+
+ return 0;
+}
+
+static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr, sec;
+
+ /*
+ * lets disable all sources which can force the DryIce unit into
+ * the "FAILURE STATE" for now
+ */
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
+ /* and lets protect them at runtime from any change */
+ di_write_busy_wait(imxdi, DCR_TDCSL, DCR);
+
+ sec = readl(imxdi->ioaddr + DTCMR);
+ if (sec != 0)
+ dev_warn(&imxdi->pdev->dev,
+ "The security violation has happend at %u seconds\n",
+ sec);
+ /*
+ * the timer cannot be set/modified if
+ * - the TCHL or TCSL bit is set in DCR
+ */
+ dcr = readl(imxdi->ioaddr + DCR);
+ if (!(dcr & DCR_TCE)) {
+ if (dcr & DCR_TCHL) {
+ /* we are out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ if (dcr & DCR_TCSL) {
+ di_what_is_to_be_done(imxdi, "main");
+ return -ENODEV;
+ }
+ }
+ /*
+ * - the timer counter stops/is stopped if
+ * - its overflow flag is set (TCO in DSR)
+ * -> clear overflow bit to make it count again
+ * - NVF is set in DSR
+ * -> clear non-valid bit to make it count again
+ * - its TCE (DCR) is cleared
+ * -> set TCE to make it count
+ * - it was never set before
+ * -> write a time into it (required again if the NVF was set)
+ */
+ /* state handled */
+ di_write_busy_wait(imxdi, DSR_NVF, DSR);
+ /* clear overflow flag */
+ di_write_busy_wait(imxdi, DSR_TCO, DSR);
+ /* enable the counter */
+ di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR);
+ /* set and trigger it to make it count */
+ di_write_busy_wait(imxdi, sec, DTCMR);
+
+ /* now prepare for the valid state */
+ return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR));
+}
+
+static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr)
+{
+ u32 dcr;
+
+ /*
+ * now we must first remove the tamper sources in order to get the
+ * device out of the "FAILURE STATE"
+ * To disable any of the following sources we need to modify the DTCR
+ */
+ if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD |
+ DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) {
+ dcr = __raw_readl(imxdi->ioaddr + DCR);
+ if (dcr & DCR_TDCHL) {
+ /*
+ * the tamper register is locked. We cannot disable the
+ * tamper detection. The TDCHL can only be reset by a
+ * DRYICE POR, but we cannot force a DRYICE POR in
+ * softwere because we are still in "FAILURE STATE".
+ * We need a DRYICE POR via battery power cycling....
+ */
+ /*
+ * out of luck!
+ * we cannot disable them without a DRYICE POR
+ */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+ if (dcr & DCR_TDCSL) {
+ /* a soft lock can be removed by a SYSTEM POR */
+ di_what_is_to_be_done(imxdi, "main");
+ return -ENODEV;
+ }
+ }
+
+ /* disable all sources */
+ di_write_busy_wait(imxdi, 0x00000000, DTCR);
+
+ /* clear the status bits now */
+ di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD |
+ DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD |
+ DSR_MCO | DSR_TCO), DSR);
+
+ dsr = readl(imxdi->ioaddr + DSR);
+ if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
+ DSR_WCF | DSR_WEF)) != 0)
+ dev_warn(&imxdi->pdev->dev,
+ "There are still some sources of pain in DSR: %08x!\n",
+ dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF |
+ DSR_WCF | DSR_WEF));
+
+ /*
+ * now we are trying to clear the "Security-violation flag" to
+ * get the DryIce out of this state
+ */
+ di_write_busy_wait(imxdi, DSR_SVF, DSR);
+
+ /* success? */
+ dsr = readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_SVF) {
+ dev_crit(&imxdi->pdev->dev,
+ "Cannot clear the security violation flag. We are ending up in an endless loop!\n");
+ /* last resort */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -ENODEV;
+ }
+
+ /*
+ * now we have left the "FAILURE STATE" and ending up in the
+ * "NON-VALID STATE" time to recover everything
+ */
+ return di_handle_invalid_state(imxdi, dsr);
+}
+
+static int di_handle_state(struct imxdi_dev *imxdi)
+{
+ int rc;
+ u32 dsr;
+
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ switch (dsr & (DSR_NVF | DSR_SVF)) {
+ case DSR_NVF:
+ dev_warn(&imxdi->pdev->dev, "Invalid stated unit detected\n");
+ rc = di_handle_invalid_state(imxdi, dsr);
+ break;
+ case DSR_SVF:
+ dev_warn(&imxdi->pdev->dev, "Failure stated unit detected\n");
+ rc = di_handle_failure_state(imxdi, dsr);
+ break;
+ case DSR_NVF | DSR_SVF:
+ dev_warn(&imxdi->pdev->dev,
+ "Failure+Invalid stated unit detected\n");
+ rc = di_handle_invalid_and_failure_state(imxdi, dsr);
+ break;
+ default:
+ dev_notice(&imxdi->pdev->dev, "Unlocked unit detected\n");
+ rc = di_handle_valid_state(imxdi, dsr);
+ }
+
+ return rc;
+}
+
/*
* enable a dryice interrupt
*/
@@ -137,8 +455,8 @@ static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
unsigned long flags;
spin_lock_irqsave(&imxdi->irq_lock, flags);
- __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
- imxdi->ioaddr + DIER);
+ writel(readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
spin_unlock_irqrestore(&imxdi->irq_lock, flags);
}
@@ -150,8 +468,8 @@ static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
unsigned long flags;
spin_lock_irqsave(&imxdi->irq_lock, flags);
- __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
- imxdi->ioaddr + DIER);
+ writel(readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
spin_unlock_irqrestore(&imxdi->irq_lock, flags);
}
@@ -169,11 +487,11 @@ static void clear_write_error(struct imxdi_dev *imxdi)
dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
/* clear the write error flag */
- __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+ writel(DSR_WEF, imxdi->ioaddr + DSR);
/* wait for it to take effect */
for (cnt = 0; cnt < 1000; cnt++) {
- if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ if ((readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
return;
udelay(10);
}
@@ -201,7 +519,7 @@ static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
imxdi->dsr = 0;
/* do the register write */
- __raw_writel(val, imxdi->ioaddr + reg);
+ writel(val, imxdi->ioaddr + reg);
/* wait for the write to finish */
ret = wait_event_interruptible_timeout(imxdi->write_wait,
@@ -235,7 +553,7 @@ static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
unsigned long now;
- now = __raw_readl(imxdi->ioaddr + DTCMR);
+ now = readl(imxdi->ioaddr + DTCMR);
rtc_time_to_tm(now, tm);
return 0;
@@ -248,14 +566,35 @@ static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
{
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcr, dsr;
int rc;
+ dcr = readl(imxdi->ioaddr + DCR);
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ if (!(dcr & DCR_TCE) || (dsr & DSR_SVF)) {
+ if (dcr & DCR_TCHL) {
+ /* we are even more out of luck */
+ di_what_is_to_be_done(imxdi, "battery");
+ return -EPERM;
+ }
+ if ((dcr & DCR_TCSL) || (dsr & DSR_SVF)) {
+ /* we are out of luck for now */
+ di_what_is_to_be_done(imxdi, "main");
+ return -EPERM;
+ }
+ }
+
/* zero the fractional part first */
rc = di_write_wait(imxdi, 0, DTCLR);
- if (rc == 0)
- rc = di_write_wait(imxdi, secs, DTCMR);
+ if (rc != 0)
+ return rc;
- return rc;
+ rc = di_write_wait(imxdi, secs, DTCMR);
+ if (rc != 0)
+ return rc;
+
+ return di_write_wait(imxdi, readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR);
}
static int dryice_rtc_alarm_irq_enable(struct device *dev,
@@ -280,17 +619,17 @@ static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
struct imxdi_dev *imxdi = dev_get_drvdata(dev);
u32 dcamr;
- dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ dcamr = readl(imxdi->ioaddr + DCAMR);
rtc_time_to_tm(dcamr, &alarm->time);
/* alarm is enabled if the interrupt is enabled */
- alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+ alarm->enabled = (readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
/* don't allow the DSR read to mess up DSR_WCF */
mutex_lock(&imxdi->write_mutex);
/* alarm is pending if the alarm flag is set */
- alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+ alarm->pending = (readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
mutex_unlock(&imxdi->write_mutex);
@@ -312,7 +651,7 @@ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return rc;
/* don't allow setting alarm in the past */
- now = __raw_readl(imxdi->ioaddr + DTCMR);
+ now = readl(imxdi->ioaddr + DTCMR);
if (alarm_time < now)
return -EINVAL;
@@ -346,7 +685,26 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
u32 dsr, dier;
irqreturn_t rc = IRQ_NONE;
- dier = __raw_readl(imxdi->ioaddr + DIER);
+ dier = readl(imxdi->ioaddr + DIER);
+ dsr = readl(imxdi->ioaddr + DSR);
+
+ /* handle the security violation event */
+ if (dier & DIER_SVIE) {
+ if (dsr & DSR_SVF) {
+ /*
+ * Disable the interrupt when this kind of event has
+ * happened.
+ * There cannot be more than one event of this type,
+ * because it needs a complex state change
+ * including a main power cycle to get again out of
+ * this state.
+ */
+ di_int_disable(imxdi, DIER_SVIE);
+ /* report the violation */
+ di_report_tamper_info(imxdi, dsr);
+ rc = IRQ_HANDLED;
+ }
+ }
/* handle write complete and write error cases */
if (dier & DIER_WCIE) {
@@ -357,7 +715,6 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
return rc;
/* DSR_WCF clears itself on DSR read */
- dsr = __raw_readl(imxdi->ioaddr + DSR);
if (dsr & (DSR_WCF | DSR_WEF)) {
/* mask the interrupt */
di_int_disable(imxdi, DIER_WCIE);
@@ -373,7 +730,6 @@ static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
/* handle the alarm case */
if (dier & DIER_CAIE) {
/* DSR_WCF clears itself on DSR read */
- dsr = __raw_readl(imxdi->ioaddr + DSR);
if (dsr & DSR_CAF) {
/* mask the interrupt */
di_int_disable(imxdi, DIER_CAIE);
@@ -446,7 +802,11 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
*/
/* mask all interrupts */
- __raw_writel(0, imxdi->ioaddr + DIER);
+ writel(0, imxdi->ioaddr + DIER);
+
+ rc = di_handle_state(imxdi);
+ if (rc != 0)
+ goto err;
rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
IRQF_SHARED, pdev->name, imxdi);
@@ -455,44 +815,6 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
goto err;
}
- /* put dryice into valid state */
- if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
- rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
- if (rc)
- goto err;
- }
-
- /* initialize alarm */
- rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
- if (rc)
- goto err;
- rc = di_write_wait(imxdi, 0, DCALR);
- if (rc)
- goto err;
-
- /* clear alarm flag */
- if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
- rc = di_write_wait(imxdi, DSR_CAF, DSR);
- if (rc)
- goto err;
- }
-
- /* the timer won't count if it has never been written to */
- if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
- rc = di_write_wait(imxdi, 0, DTCMR);
- if (rc)
- goto err;
- }
-
- /* start keeping time */
- if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
- rc = di_write_wait(imxdi,
- __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
- DCR);
- if (rc)
- goto err;
- }
-
platform_set_drvdata(pdev, imxdi);
imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&dryice_rtc_ops, THIS_MODULE);
@@ -516,7 +838,7 @@ static int __exit dryice_rtc_remove(struct platform_device *pdev)
flush_work(&imxdi->work);
/* mask all interrupts */
- __raw_writel(0, imxdi->ioaddr + DIER);
+ writel(0, imxdi->ioaddr + DIER);
clk_disable_unprepare(imxdi->clk);
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index c3c549d..aa3b8f1 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -370,22 +370,15 @@ isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
struct rtc_time *alarm_tm = &alarm->time;
u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
const int offs = ISL1208_REG_SCA;
- unsigned long rtc_secs, alarm_secs;
struct rtc_time rtc_tm;
int err, enable;
err = isl1208_i2c_read_time(client, &rtc_tm);
if (err)
return err;
- err = rtc_tm_to_time(&rtc_tm, &rtc_secs);
- if (err)
- return err;
- err = rtc_tm_to_time(alarm_tm, &alarm_secs);
- if (err)
- return err;
/* If the alarm time is before the current time disable the alarm */
- if (!alarm->enabled || alarm_secs <= rtc_secs)
+ if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0)
enable = 0x00;
else
enable = 0x80;
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index 4804985..b2a7607 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -234,6 +234,7 @@ static struct i2c_device_id max6900_id[] = {
{ "max6900", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, max6900_id);
static struct i2c_driver max6900_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 7632a87..7184a0e 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -511,6 +511,7 @@ static const struct platform_device_id rtc_id[] = {
{ "max77686-rtc", 0 },
{},
};
+MODULE_DEVICE_TABLE(platform, rtc_id);
static struct platform_driver max77686_rtc_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c
index 7f8adf8..82ffcc5 100644
--- a/drivers/rtc/rtc-max77802.c
+++ b/drivers/rtc/rtc-max77802.c
@@ -484,6 +484,7 @@ static const struct platform_device_id rtc_id[] = {
{ "max77802-rtc", 0 },
{},
};
+MODULE_DEVICE_TABLE(platform, rtc_id);
static struct platform_driver max77802_rtc_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
index 5726ef7..30804b0 100644
--- a/drivers/rtc/rtc-max8998.c
+++ b/drivers/rtc/rtc-max8998.c
@@ -309,6 +309,7 @@ static const struct platform_device_id max8998_rtc_id[] = {
{ "lp3974-rtc", TYPE_LP3974 },
{ }
};
+MODULE_DEVICE_TABLE(platform, max8998_rtc_id);
static struct platform_driver max8998_rtc_driver = {
.driver = {
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index 32df1d8..a658680 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -216,7 +216,7 @@ static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
s1970 = rtc_tm_to_time64(&alarm->time);
- dev_dbg(dev, "%s: o%2.s %lld\n", __func__, alarm->enabled ? "n" : "ff",
+ dev_dbg(dev, "%s: %s %lld\n", __func__, alarm->enabled ? "on" : "off",
(long long)s1970);
ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
new file mode 100644
index 0000000..c0090b6
--- /dev/null
+++ b/drivers/rtc/rtc-mt6397.c
@@ -0,0 +1,395 @@
+/*
+* Copyright (c) 2014-2015 MediaTek Inc.
+* Author: Tianping.Fang <tianping.fang@mediatek.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/mfd/mt6397/core.h>
+
+#define RTC_BBPU 0x0000
+#define RTC_BBPU_CBUSY BIT(6)
+
+#define RTC_WRTGR 0x003c
+
+#define RTC_IRQ_STA 0x0002
+#define RTC_IRQ_STA_AL BIT(0)
+#define RTC_IRQ_STA_LP BIT(3)
+
+#define RTC_IRQ_EN 0x0004
+#define RTC_IRQ_EN_AL BIT(0)
+#define RTC_IRQ_EN_ONESHOT BIT(2)
+#define RTC_IRQ_EN_LP BIT(3)
+#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL)
+
+#define RTC_AL_MASK 0x0008
+#define RTC_AL_MASK_DOW BIT(4)
+
+#define RTC_TC_SEC 0x000a
+/* Min, Hour, Dom... register offset to RTC_TC_SEC */
+#define RTC_OFFSET_SEC 0
+#define RTC_OFFSET_MIN 1
+#define RTC_OFFSET_HOUR 2
+#define RTC_OFFSET_DOM 3
+#define RTC_OFFSET_DOW 4
+#define RTC_OFFSET_MTH 5
+#define RTC_OFFSET_YEAR 6
+#define RTC_OFFSET_COUNT 7
+
+#define RTC_AL_SEC 0x0018
+
+#define RTC_PDN2 0x002e
+#define RTC_PDN2_PWRON_ALARM BIT(4)
+
+#define RTC_MIN_YEAR 1968
+#define RTC_BASE_YEAR 1900
+#define RTC_NUM_YEARS 128
+#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR)
+
+struct mt6397_rtc {
+ struct device *dev;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+ struct regmap *regmap;
+ int irq;
+ u32 addr_base;
+};
+
+static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc)
+{
+ unsigned long timeout = jiffies + HZ;
+ int ret;
+ u32 data;
+
+ ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_WRTGR, 1);
+ if (ret < 0)
+ return ret;
+
+ while (1) {
+ ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_BBPU,
+ &data);
+ if (ret < 0)
+ break;
+ if (!(data & RTC_BBPU_CBUSY))
+ break;
+ if (time_after(jiffies, timeout)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ cpu_relax();
+ }
+
+ return ret;
+}
+
+static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data)
+{
+ struct mt6397_rtc *rtc = data;
+ u32 irqsta, irqen;
+ int ret;
+
+ ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_STA, &irqsta);
+ if ((ret >= 0) && (irqsta & RTC_IRQ_STA_AL)) {
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ irqen = irqsta & ~RTC_IRQ_EN_AL;
+ mutex_lock(&rtc->lock);
+ if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN,
+ irqen) < 0)
+ mtk_rtc_write_trigger(rtc);
+ mutex_unlock(&rtc->lock);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int __mtk_rtc_read_time(struct mt6397_rtc *rtc,
+ struct rtc_time *tm, int *sec)
+{
+ int ret;
+ u16 data[RTC_OFFSET_COUNT];
+
+ mutex_lock(&rtc->lock);
+ ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC,
+ data, RTC_OFFSET_COUNT);
+ if (ret < 0)
+ goto exit;
+
+ tm->tm_sec = data[RTC_OFFSET_SEC];
+ tm->tm_min = data[RTC_OFFSET_MIN];
+ tm->tm_hour = data[RTC_OFFSET_HOUR];
+ tm->tm_mday = data[RTC_OFFSET_DOM];
+ tm->tm_mon = data[RTC_OFFSET_MTH];
+ tm->tm_year = data[RTC_OFFSET_YEAR];
+
+ ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, sec);
+exit:
+ mutex_unlock(&rtc->lock);
+ return ret;
+}
+
+static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ time64_t time;
+ struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+ int days, sec, ret;
+
+ do {
+ ret = __mtk_rtc_read_time(rtc, tm, &sec);
+ if (ret < 0)
+ goto exit;
+ } while (sec < tm->tm_sec);
+
+ /* HW register use 7 bits to store year data, minus
+ * RTC_MIN_YEAR_OFFSET before write year data to register, and plus
+ * RTC_MIN_YEAR_OFFSET back after read year from register
+ */
+ tm->tm_year += RTC_MIN_YEAR_OFFSET;
+
+ /* HW register start mon from one, but tm_mon start from zero. */
+ tm->tm_mon--;
+ time = rtc_tm_to_time64(tm);
+
+ /* rtc_tm_to_time64 covert Gregorian date to seconds since
+ * 01-01-1970 00:00:00, and this date is Thursday.
+ */
+ days = div_s64(time, 86400);
+ tm->tm_wday = (days + 4) % 7;
+
+exit:
+ return ret;
+}
+
+static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+ u16 data[RTC_OFFSET_COUNT];
+
+ tm->tm_year -= RTC_MIN_YEAR_OFFSET;
+ tm->tm_mon++;
+
+ data[RTC_OFFSET_SEC] = tm->tm_sec;
+ data[RTC_OFFSET_MIN] = tm->tm_min;
+ data[RTC_OFFSET_HOUR] = tm->tm_hour;
+ data[RTC_OFFSET_DOM] = tm->tm_mday;
+ data[RTC_OFFSET_MTH] = tm->tm_mon;
+ data[RTC_OFFSET_YEAR] = tm->tm_year;
+
+ mutex_lock(&rtc->lock);
+ ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_TC_SEC,
+ data, RTC_OFFSET_COUNT);
+ if (ret < 0)
+ goto exit;
+
+ /* Time register write to hardware after call trigger function */
+ ret = mtk_rtc_write_trigger(rtc);
+
+exit:
+ mutex_unlock(&rtc->lock);
+ return ret;
+}
+
+static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_time *tm = &alm->time;
+ struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+ u32 irqen, pdn2;
+ int ret;
+ u16 data[RTC_OFFSET_COUNT];
+
+ mutex_lock(&rtc->lock);
+ ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, &irqen);
+ if (ret < 0)
+ goto err_exit;
+ ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_PDN2, &pdn2);
+ if (ret < 0)
+ goto err_exit;
+
+ ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC,
+ data, RTC_OFFSET_COUNT);
+ if (ret < 0)
+ goto err_exit;
+
+ alm->enabled = !!(irqen & RTC_IRQ_EN_AL);
+ alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM);
+ mutex_unlock(&rtc->lock);
+
+ tm->tm_sec = data[RTC_OFFSET_SEC];
+ tm->tm_min = data[RTC_OFFSET_MIN];
+ tm->tm_hour = data[RTC_OFFSET_HOUR];
+ tm->tm_mday = data[RTC_OFFSET_DOM];
+ tm->tm_mon = data[RTC_OFFSET_MTH];
+ tm->tm_year = data[RTC_OFFSET_YEAR];
+
+ tm->tm_year += RTC_MIN_YEAR_OFFSET;
+ tm->tm_mon--;
+
+ return 0;
+err_exit:
+ mutex_unlock(&rtc->lock);
+ return ret;
+}
+
+static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_time *tm = &alm->time;
+ struct mt6397_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+ u16 data[RTC_OFFSET_COUNT];
+
+ tm->tm_year -= RTC_MIN_YEAR_OFFSET;
+ tm->tm_mon++;
+
+ data[RTC_OFFSET_SEC] = tm->tm_sec;
+ data[RTC_OFFSET_MIN] = tm->tm_min;
+ data[RTC_OFFSET_HOUR] = tm->tm_hour;
+ data[RTC_OFFSET_DOM] = tm->tm_mday;
+ data[RTC_OFFSET_MTH] = tm->tm_mon;
+ data[RTC_OFFSET_YEAR] = tm->tm_year;
+
+ mutex_lock(&rtc->lock);
+ if (alm->enabled) {
+ ret = regmap_bulk_write(rtc->regmap,
+ rtc->addr_base + RTC_AL_SEC,
+ data, RTC_OFFSET_COUNT);
+ if (ret < 0)
+ goto exit;
+ ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_AL_MASK,
+ RTC_AL_MASK_DOW);
+ if (ret < 0)
+ goto exit;
+ ret = regmap_update_bits(rtc->regmap,
+ rtc->addr_base + RTC_IRQ_EN,
+ RTC_IRQ_EN_ONESHOT_AL,
+ RTC_IRQ_EN_ONESHOT_AL);
+ if (ret < 0)
+ goto exit;
+ } else {
+ ret = regmap_update_bits(rtc->regmap,
+ rtc->addr_base + RTC_IRQ_EN,
+ RTC_IRQ_EN_ONESHOT_AL, 0);
+ if (ret < 0)
+ goto exit;
+ }
+
+ /* All alarm time register write to hardware after calling
+ * mtk_rtc_write_trigger. This can avoid race condition if alarm
+ * occur happen during writing alarm time register.
+ */
+ ret = mtk_rtc_write_trigger(rtc);
+exit:
+ mutex_unlock(&rtc->lock);
+ return ret;
+}
+
+static struct rtc_class_ops mtk_rtc_ops = {
+ .read_time = mtk_rtc_read_time,
+ .set_time = mtk_rtc_set_time,
+ .read_alarm = mtk_rtc_read_alarm,
+ .set_alarm = mtk_rtc_set_alarm,
+};
+
+static int mtk_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
+ struct mt6397_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->addr_base = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ rtc->irq = irq_create_mapping(mt6397_chip->irq_domain, res->start);
+ if (rtc->irq <= 0)
+ return -EINVAL;
+
+ rtc->regmap = mt6397_chip->regmap;
+ rtc->dev = &pdev->dev;
+ mutex_init(&rtc->lock);
+
+ platform_set_drvdata(pdev, rtc);
+
+ ret = request_threaded_irq(rtc->irq, NULL,
+ mtk_rtc_irq_handler_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+ "mt6397-rtc", rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ rtc->irq, ret);
+ goto out_dispose_irq;
+ }
+
+ rtc->rtc_dev = rtc_device_register("mt6397-rtc", &pdev->dev,
+ &mtk_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ dev_err(&pdev->dev, "register rtc device failed\n");
+ ret = PTR_ERR(rtc->rtc_dev);
+ goto out_free_irq;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+out_free_irq:
+ free_irq(rtc->irq, rtc->rtc_dev);
+out_dispose_irq:
+ irq_dispose_mapping(rtc->irq);
+ return ret;
+}
+
+static int mtk_rtc_remove(struct platform_device *pdev)
+{
+ struct mt6397_rtc *rtc = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(rtc->rtc_dev);
+ free_irq(rtc->irq, rtc->rtc_dev);
+ irq_dispose_mapping(rtc->irq);
+
+ return 0;
+}
+
+static const struct of_device_id mt6397_rtc_of_match[] = {
+ { .compatible = "mediatek,mt6397-rtc", },
+ { }
+};
+
+static struct platform_driver mtk_rtc_driver = {
+ .driver = {
+ .name = "mt6397-rtc",
+ .of_match_table = mt6397_rtc_of_match,
+ },
+ .probe = mtk_rtc_probe,
+ .remove = mtk_rtc_remove,
+};
+
+module_platform_driver(mtk_rtc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Tianping Fang <tianping.fang@mediatek.com>");
+MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC");
+MODULE_ALIAS("platform:mt6397-rtc");
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 4237622..7f50d2e 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
+#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/of.h>
@@ -24,7 +25,7 @@
#define RTC_MINUTES_OFFS 8
#define RTC_HOURS_OFFS 16
#define RTC_WDAY_OFFS 24
-#define RTC_HOURS_12H_MODE (1 << 22) /* 12 hours mode */
+#define RTC_HOURS_12H_MODE BIT(22) /* 12 hour mode */
#define RTC_DATE_REG_OFFS 4
#define RTC_MDAY_OFFS 0
@@ -33,7 +34,7 @@
#define RTC_ALARM_TIME_REG_OFFS 8
#define RTC_ALARM_DATE_REG_OFFS 0xc
-#define RTC_ALARM_VALID (1 << 7)
+#define RTC_ALARM_VALID BIT(7)
#define RTC_ALARM_INTERRUPT_MASK_REG_OFFS 0x10
#define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS 0x14
@@ -77,7 +78,7 @@ static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm)
second = rtc_time & 0x7f;
minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
- hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
+ hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */
wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
day = rtc_date & 0x3f;
@@ -108,7 +109,7 @@ static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
second = rtc_time & 0x7f;
minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
- hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
+ hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */
wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
day = rtc_date & 0x3f;
@@ -239,10 +240,10 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
if (!IS_ERR(pdata->clk))
clk_prepare_enable(pdata->clk);
- /* make sure the 24 hours mode is enabled */
+ /* make sure the 24 hour mode is enabled */
rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);
if (rtc_time & RTC_HOURS_12H_MODE) {
- dev_err(&pdev->dev, "24 Hours mode not supported.\n");
+ dev_err(&pdev->dev, "12 Hour mode is enabled but not supported.\n");
ret = -EINVAL;
goto out;
}
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 09d422b..5fc292c 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -84,7 +84,7 @@ struct rtc_plat_data {
enum imx_rtc_type devtype;
};
-static struct platform_device_id imx_rtc_devtype[] = {
+static const struct platform_device_id imx_rtc_devtype[] = {
{
.name = "imx1-rtc",
.driver_data = IMX1_RTC,
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
index 3b01d56..7ea2c47 100644
--- a/drivers/rtc/rtc-palmas.c
+++ b/drivers/rtc/rtc-palmas.c
@@ -239,7 +239,7 @@ static int palmas_rtc_probe(struct platform_device *pdev)
struct palmas_rtc *palmas_rtc = NULL;
int ret;
bool enable_bb_charging = false;
- bool high_bb_charging;
+ bool high_bb_charging = false;
if (pdev->dev.of_node) {
enable_bb_charging = of_property_read_bool(pdev->dev.of_node,
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 0ba7e59..8bba022 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -22,7 +22,7 @@
#include <linux/of.h>
#include <linux/err.h>
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.4"
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
@@ -202,8 +202,9 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
pcf8563->voltage_low = 1;
- dev_info(&client->dev,
+ dev_err(&client->dev,
"low voltage detected, date/time is not reliable.\n");
+ return -EINVAL;
}
dev_dbg(&client->dev,
@@ -234,12 +235,6 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- /* the clock can give out invalid datetime, but we cannot return
- * -EINVAL otherwise hwclock will refuse to set the time on bootup.
- */
- if (rtc_valid_tm(tm) < 0)
- dev_err(&client->dev, "retrieved date/time is not valid.\n");
-
return 0;
}
@@ -363,13 +358,13 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
struct i2c_client *client = to_i2c_client(dev);
unsigned char buf[4];
int err;
- unsigned long alarm_time;
/* The alarm has no seconds, round up to nearest minute */
if (tm->time.tm_sec) {
- rtc_tm_to_time(&tm->time, &alarm_time);
- alarm_time += 60-tm->time.tm_sec;
- rtc_time_to_tm(alarm_time, &tm->time);
+ time64_t alarm_time = rtc_tm_to_time64(&tm->time);
+
+ alarm_time += 60 - tm->time.tm_sec;
+ rtc_time64_to_tm(alarm_time, &tm->time);
}
dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
@@ -437,7 +432,7 @@ static int pcf8563_probe(struct i2c_client *client,
}
err = pcf8563_get_alarm_mode(client, NULL, &alm_pending);
- if (err < 0) {
+ if (err) {
dev_err(&client->dev, "%s: read error\n", __func__);
return err;
}
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 76cbad7..a0f8323 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -772,18 +772,6 @@ static struct s3c_rtc_data const s3c6410_rtc_data = {
.disable = s3c6410_rtc_disable,
};
-static struct s3c_rtc_data const exynos3250_rtc_data = {
- .max_user_freq = 32768,
- .needs_src_clk = true,
- .irq_handler = s3c6410_rtc_irq,
- .set_freq = s3c6410_rtc_setfreq,
- .enable_tick = s3c6410_rtc_enable_tick,
- .save_tick_cnt = s3c6410_rtc_save_tick_cnt,
- .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
- .enable = s3c24xx_rtc_enable,
- .disable = s3c6410_rtc_disable,
-};
-
static const struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
@@ -799,7 +787,7 @@ static const struct of_device_id s3c_rtc_dt_match[] = {
.data = (void *)&s3c6410_rtc_data,
}, {
.compatible = "samsung,exynos3250-rtc",
- .data = (void *)&exynos3250_rtc_data,
+ .data = (void *)&s3c6410_rtc_data,
},
{ /* sentinel */ },
};
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index 0479e80..d87a85c 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -322,6 +322,13 @@ static int snvs_rtc_suspend(struct device *dev)
if (device_may_wakeup(dev))
enable_irq_wake(data->irq);
+ return 0;
+}
+
+static int snvs_rtc_suspend_noirq(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
if (data->clk)
clk_disable_unprepare(data->clk);
@@ -331,23 +338,28 @@ static int snvs_rtc_suspend(struct device *dev)
static int snvs_rtc_resume(struct device *dev)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
- int ret;
if (device_may_wakeup(dev))
- disable_irq_wake(data->irq);
+ return disable_irq_wake(data->irq);
- if (data->clk) {
- ret = clk_prepare_enable(data->clk);
- if (ret)
- return ret;
- }
+ return 0;
+}
+
+static int snvs_rtc_resume_noirq(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (data->clk)
+ return clk_prepare_enable(data->clk);
return 0;
}
static const struct dev_pm_ops snvs_rtc_pm_ops = {
- .suspend_noirq = snvs_rtc_suspend,
- .resume_noirq = snvs_rtc_resume,
+ .suspend = snvs_rtc_suspend,
+ .suspend_noirq = snvs_rtc_suspend_noirq,
+ .resume = snvs_rtc_resume,
+ .resume_noirq = snvs_rtc_resume_noirq,
};
#define SNVS_RTC_PM_OPS (&snvs_rtc_pm_ops)
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index d2cdb98..f05ef85 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -358,12 +358,6 @@ static int spear_rtc_probe(struct platform_device *pdev)
int status = 0;
int irq;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no resource defined\n");
- return -EBUSY;
- }
-
config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
if (!config)
return -ENOMEM;
@@ -383,6 +377,7 @@ static int spear_rtc_probe(struct platform_device *pdev)
return status;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
config->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(config->ioaddr))
return PTR_ERR(config->ioaddr);
diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c
index 6e678fa..52543ae 100644
--- a/drivers/rtc/rtc-sunxi.c
+++ b/drivers/rtc/rtc-sunxi.c
@@ -269,14 +269,13 @@ static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
struct rtc_time *alrm_tm = &wkalrm->time;
struct rtc_time tm_now;
- u32 alrm = 0;
- unsigned long time_now = 0;
- unsigned long time_set = 0;
- unsigned long time_gap = 0;
- unsigned long time_gap_day = 0;
- unsigned long time_gap_hour = 0;
- unsigned long time_gap_min = 0;
- int ret = 0;
+ u32 alrm;
+ time64_t diff;
+ unsigned long time_gap;
+ unsigned long time_gap_day;
+ unsigned long time_gap_hour;
+ unsigned long time_gap_min;
+ int ret;
ret = sunxi_rtc_gettime(dev, &tm_now);
if (ret < 0) {
@@ -284,14 +283,18 @@ static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
return -EINVAL;
}
- rtc_tm_to_time(alrm_tm, &time_set);
- rtc_tm_to_time(&tm_now, &time_now);
- if (time_set <= time_now) {
+ diff = rtc_tm_sub(alrm_tm, &tm_now);
+ if (diff <= 0) {
dev_err(dev, "Date to set in the past\n");
return -EINVAL;
}
- time_gap = time_set - time_now;
+ if (diff > 255 * SEC_IN_DAY) {
+ dev_err(dev, "Day must be in the range 0 - 255\n");
+ return -EINVAL;
+ }
+
+ time_gap = diff;
time_gap_day = time_gap / SEC_IN_DAY;
time_gap -= time_gap_day * SEC_IN_DAY;
time_gap_hour = time_gap / SEC_IN_HOUR;
@@ -299,11 +302,6 @@ static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
time_gap_min = time_gap / SEC_IN_MIN;
time_gap -= time_gap_min * SEC_IN_MIN;
- if (time_gap_day > 255) {
- dev_err(dev, "Day must be in the range 0 - 255\n");
- return -EINVAL;
- }
-
sunxi_rtc_setaie(0, chip);
writel(0, chip->base + SUNXI_ALRM_DHMS);
usleep_range(100, 300);
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index bfbfa7e..f9f9709 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -49,18 +49,13 @@ struct v3020_chip_ops {
#define V3020_RD 2
#define V3020_IO 3
-struct v3020_gpio {
- const char *name;
- unsigned int gpio;
-};
-
struct v3020 {
/* MMIO access */
void __iomem *ioaddress;
int leftshift;
/* GPIO access */
- struct v3020_gpio *gpio;
+ struct gpio *gpio;
struct v3020_chip_ops *ops;
@@ -107,48 +102,34 @@ static struct v3020_chip_ops v3020_mmio_ops = {
.write_bit = v3020_mmio_write_bit,
};
-static struct v3020_gpio v3020_gpio[] = {
- { "RTC CS", 0 },
- { "RTC WR", 0 },
- { "RTC RD", 0 },
- { "RTC IO", 0 },
+static struct gpio v3020_gpio[] = {
+ { 0, GPIOF_OUT_INIT_HIGH, "RTC CS"},
+ { 0, GPIOF_OUT_INIT_HIGH, "RTC WR"},
+ { 0, GPIOF_OUT_INIT_HIGH, "RTC RD"},
+ { 0, GPIOF_OUT_INIT_HIGH, "RTC IO"},
};
static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev,
struct v3020_platform_data *pdata)
{
- int i, err;
+ int err;
v3020_gpio[V3020_CS].gpio = pdata->gpio_cs;
v3020_gpio[V3020_WR].gpio = pdata->gpio_wr;
v3020_gpio[V3020_RD].gpio = pdata->gpio_rd;
v3020_gpio[V3020_IO].gpio = pdata->gpio_io;
- for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) {
- err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name);
- if (err)
- goto err_request;
+ err = gpio_request_array(v3020_gpio, ARRAY_SIZE(v3020_gpio));
- gpio_direction_output(v3020_gpio[i].gpio, 1);
- }
-
- chip->gpio = v3020_gpio;
-
- return 0;
-
-err_request:
- while (--i >= 0)
- gpio_free(v3020_gpio[i].gpio);
+ if (!err)
+ chip->gpio = v3020_gpio;
return err;
}
static void v3020_gpio_unmap(struct v3020 *chip)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++)
- gpio_free(v3020_gpio[i].gpio);
+ gpio_free_array(v3020_gpio, ARRAY_SIZE(v3020_gpio));
}
static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit)
diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
index 7728d5e..b4a68ff 100644
--- a/drivers/rtc/systohc.c
+++ b/drivers/rtc/systohc.c
@@ -31,7 +31,7 @@ int rtc_set_ntp_time(struct timespec64 now)
else
rtc_time64_to_tm(now.tv_sec + 1, &tm);
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
if (rtc) {
/* rtc_hctosys exclusively uses UTC, so we call set_time here,
* not set_mmss. */
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index 90f39f7..ef1d9fb 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -99,9 +99,8 @@ void dasd_gendisk_free(struct dasd_block *block)
int dasd_scan_partitions(struct dasd_block *block)
{
struct block_device *bdev;
- int retry, rc;
+ int rc;
- retry = 5;
bdev = bdget_disk(block->gdp, 0);
if (!bdev) {
DBF_DEV_EVENT(DBF_ERR, block->base, "%s",
@@ -116,19 +115,11 @@ int dasd_scan_partitions(struct dasd_block *block)
rc);
return -ENODEV;
}
- /*
- * See fs/partition/check.c:register_disk,rescan_partitions
- * Can't call rescan_partitions directly. Use ioctl.
- */
- rc = ioctl_by_bdev(bdev, BLKRRPART, 0);
- while (rc == -EBUSY && retry > 0) {
- schedule();
- rc = ioctl_by_bdev(bdev, BLKRRPART, 0);
- retry--;
+
+ rc = blkdev_reread_part(bdev);
+ if (rc)
DBF_DEV_EVENT(DBF_ERR, block->base,
- "scan partitions error, retry %d rc %d",
- retry, rc);
- }
+ "scan partitions error, rc %d", rc);
/*
* Since the matching blkdev_put call to the blkdev_get in
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
index d726058..1456278 100644
--- a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
@@ -55,9 +55,7 @@ truncate_complete_page(struct address_space *mapping, struct page *page)
if (PagePrivate(page))
page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE);
- if (TestClearPageDirty(page))
- account_page_cleaned(page, mapping);
-
+ cancel_dirty_page(page);
ClearPageMappedToDisk(page);
ll_delete_from_page_cache(page);
}
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 96498b7..1469768 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -25,8 +25,6 @@ source "drivers/staging/media/cxd2099/Kconfig"
source "drivers/staging/media/davinci_vpfe/Kconfig"
-source "drivers/staging/media/dt3155v4l/Kconfig"
-
source "drivers/staging/media/mn88472/Kconfig"
source "drivers/staging/media/mn88473/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index a9006bc..34c557b 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,7 +1,6 @@
obj-$(CONFIG_I2C_BCM2048) += bcm2048/
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
obj-$(CONFIG_LIRC_STAGING) += lirc/
-obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_DVB_MN88472) += mn88472/
diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c
index e9d0691..5e11a78 100644
--- a/drivers/staging/media/bcm2048/radio-bcm2048.c
+++ b/drivers/staging/media/bcm2048/radio-bcm2048.c
@@ -2593,7 +2593,7 @@ static int bcm2048_i2c_driver_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bcm2048_device *bdev;
- int err, skip_release = 0;
+ int err;
bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
if (!bdev) {
@@ -2646,7 +2646,6 @@ free_sysfs:
bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs));
free_registration:
video_unregister_device(&bdev->videodev);
- skip_release = 1;
free_irq:
if (client->irq)
free_irq(client->irq, bdev);
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index b649813..acb293e 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -321,6 +321,7 @@ static int resizer_configure_output_win(struct vpfe_resizer_device *resizer)
outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ memset(&output_specs, 0x0, sizeof(struct vpfe_rsz_output_spec));
output_specs.vst_y = param->user_config.vst;
if (outformat->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
output_specs.vst_c = param->user_config.vst;
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
index 2632a80..8ad8d74 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
@@ -67,8 +67,6 @@ struct vpfe_device {
/* CCDC IRQs used when CCDC/ISIF output to SDRAM */
unsigned int ccdc_irq0;
unsigned int ccdc_irq1;
- /* maximum video memory that is available*/
- unsigned int video_limit;
/* media device */
struct media_device media_dev;
/* ccdc subdevice */
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 06d48d5..87048a1 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -27,9 +27,6 @@
#include "vpfe.h"
#include "vpfe_mc_capture.h"
-/* minimum number of buffers needed in cont-mode */
-#define MIN_NUM_BUFFERS 3
-
static int debug;
/* get v4l2 subdev pointer to external subdev which is active */
@@ -473,7 +470,7 @@ void vpfe_video_process_buffer_complete(struct vpfe_video_device *video)
{
struct vpfe_pipeline *pipe = &video->pipe;
- do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp);
+ v4l2_get_timestamp(&video->cur_frm->vb.v4l2_buf.timestamp);
vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE);
if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
video->cur_frm = video->next_frm;
@@ -1088,20 +1085,14 @@ vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct vpfe_fh *fh = vb2_get_drv_priv(vq);
struct vpfe_video_device *video = fh->video;
struct vpfe_device *vpfe_dev = video->vpfe_dev;
- struct vpfe_pipeline *pipe = &video->pipe;
unsigned long size;
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n");
size = video->fmt.fmt.pix.sizeimage;
- if (vpfe_dev->video_limit) {
- while (size * *nbuffers > vpfe_dev->video_limit)
- (*nbuffers)--;
- }
- if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) {
- if (*nbuffers < MIN_NUM_BUFFERS)
- *nbuffers = MIN_NUM_BUFFERS;
- }
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+
*nplanes = 1;
sizes[0] = size;
alloc_ctxs[0] = video->alloc_ctx;
@@ -1346,6 +1337,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret) {
diff --git a/drivers/staging/media/dt3155v4l/Kconfig b/drivers/staging/media/dt3155v4l/Kconfig
deleted file mode 100644
index 2d49600..0000000
--- a/drivers/staging/media/dt3155v4l/Kconfig
+++ /dev/null
@@ -1,29 +0,0 @@
-config VIDEO_DT3155
- tristate "DT3155 frame grabber, Video4Linux interface"
- 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 dt3155v4l.
-
-config DT3155_CCIR
- bool "Selects CCIR/50Hz vertical refresh"
- depends on VIDEO_DT3155
- default y
- ---help---
- Select it for CCIR/50Hz (European region),
- or leave it unselected for RS-170/60Hz (North America).
-
-config DT3155_STREAMING
- bool "Selects streaming capture method"
- depends on VIDEO_DT3155
- default y
- ---help---
- Select it if you want to use streaming of memory mapped buffers
- or leave it unselected if you want to use read method (one copy more).
diff --git a/drivers/staging/media/dt3155v4l/Makefile b/drivers/staging/media/dt3155v4l/Makefile
deleted file mode 100644
index ce7a3ec..0000000
--- a/drivers/staging/media/dt3155v4l/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
deleted file mode 100644
index 52a8ffe..0000000
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ /dev/null
@@ -1,981 +0,0 @@
-/***************************************************************************
- * 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. *
- * *
- * 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., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/slab.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-common.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "dt3155v4l.h"
-
-#define DT3155_DEVICE_ID 0x1223
-
-/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */
-#define DT3155_CHUNK_SIZE (1U << 22)
-
-#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN)
-
-#define DT3155_BUF_SIZE (768 * 576)
-
-#ifdef CONFIG_DT3155_STREAMING
-#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING
-#else
-#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE
-#endif
-
-/* global initializers (for all boards) */
-#ifdef CONFIG_DT3155_CCIR
-static const u8 csr2_init = VT_50HZ;
-#define DT3155_CURRENT_NORM V4L2_STD_625_50
-static const unsigned int img_width = 768;
-static const unsigned int img_height = 576;
-static const unsigned int frames_per_sec = 25;
-static const struct v4l2_fmtdesc frame_std[] = {
- {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .flags = 0,
- .description = "CCIR/50Hz 8 bits gray",
- .pixelformat = V4L2_PIX_FMT_GREY,
- },
-};
-#else
-static const u8 csr2_init = VT_60HZ;
-#define DT3155_CURRENT_NORM V4L2_STD_525_60
-static const unsigned int img_width = 640;
-static const unsigned int img_height = 480;
-static const unsigned int frames_per_sec = 30;
-static const struct v4l2_fmtdesc frame_std[] = {
- {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .flags = 0,
- .description = "RS-170/60Hz 8 bits gray",
- .pixelformat = V4L2_PIX_FMT_GREY,
- },
-};
-#endif
-
-#define NUM_OF_FORMATS ARRAY_SIZE(frame_std)
-
-static u8 config_init = ACQ_MODE_EVEN;
-
-/**
- * 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 writting 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 writting 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/writting 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_start_acq(struct dt3155_priv *pd)
-{
- struct vb2_buffer *vb = pd->curr_buf;
- dma_addr_t dma_addr;
-
- dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- iowrite32(dma_addr, pd->regs + EVEN_DMA_START);
- iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START);
- iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE);
- iowrite32(img_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; /* success */
-}
-
-/*
- * driver-specific callbacks (vb2_ops)
- */
-static int
-dt3155_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
- unsigned int *num_buffers, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
- void *ret;
-
- if (*num_buffers == 0)
- *num_buffers = 1;
- *num_planes = 1;
- sizes[0] = img_width * img_height;
- if (pd->q->alloc_ctx[0])
- return 0;
- ret = vb2_dma_contig_init_ctx(&pd->pdev->dev);
- if (IS_ERR(ret))
- return PTR_ERR(ret);
- pd->q->alloc_ctx[0] = ret;
- return 0;
-}
-
-static void
-dt3155_wait_prepare(struct vb2_queue *q)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
-
- mutex_unlock(pd->vdev.lock);
-}
-
-static void
-dt3155_wait_finish(struct vb2_queue *q)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(q);
-
- mutex_lock(pd->vdev.lock);
-}
-
-static int
-dt3155_buf_prepare(struct vb2_buffer *vb)
-{
- vb2_set_plane_payload(vb, 0, img_width * img_height);
- 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);
- 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);
- msleep(45); /* irq hendler will stop the hardware */
-}
-
-static void
-dt3155_buf_queue(struct vb2_buffer *vb)
-{
- struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);
-
- /* pd->q->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 = vb;
- dt3155_start_acq(pd);
- }
- spin_unlock_irq(&pd->lock);
-}
-/*
- * end driver-specific callbacks
- */
-
-static const struct vb2_ops q_ops = {
- .queue_setup = dt3155_queue_setup,
- .wait_prepare = dt3155_wait_prepare,
- .wait_finish = dt3155_wait_finish,
- .buf_prepare = dt3155_buf_prepare,
- .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);
- ipd->field_count++;
- return IRQ_HANDLED; /* start of field irq */
- }
- if ((tmp & FLD_START) && (tmp & FLD_END_ODD))
- ipd->stats.start_before_end++;
- /* check for corrupted fields */
-/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */
-/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */
- tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD);
- if (tmp) {
- ipd->stats.corrupted_fields++;
- 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) {
- v4l2_get_timestamp(&ipd->curr_buf->v4l2_buf.timestamp);
- ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1;
- vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE);
- }
-
- if (!ipd->q->streaming || list_empty(&ipd->dmaq))
- goto stop_dma;
- ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
- list_del(&ivb->done_entry);
- ipd->curr_buf = ivb;
- dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);
- iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);
- iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START);
- iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE);
- iowrite32(img_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;
-
-stop_dma:
- ipd->curr_buf = NULL;
- /* stop the board */
- write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2);
- iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
- FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1);
- /* disable interrupts, clear all irq flags */
- iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
- spin_unlock(&ipd->lock);
- return IRQ_HANDLED;
-}
-
-static int
-dt3155_open(struct file *filp)
-{
- int ret = 0;
- struct dt3155_priv *pd = video_drvdata(filp);
-
- if (mutex_lock_interruptible(&pd->mux))
- return -ERESTARTSYS;
- if (!pd->users) {
- pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL);
- if (!pd->q) {
- ret = -ENOMEM;
- goto err_alloc_queue;
- }
- pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pd->q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- pd->q->io_modes = VB2_READ | VB2_MMAP;
- pd->q->ops = &q_ops;
- pd->q->mem_ops = &vb2_dma_contig_memops;
- pd->q->drv_priv = pd;
- pd->curr_buf = NULL;
- pd->field_count = 0;
- ret = vb2_queue_init(pd->q);
- if (ret < 0)
- goto err_request_irq;
- INIT_LIST_HEAD(&pd->dmaq);
- spin_lock_init(&pd->lock);
- /* disable all irqs, clear all irq flags */
- iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,
- pd->regs + INT_CSR);
- ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even,
- IRQF_SHARED, DT3155_NAME, pd);
- if (ret)
- goto err_request_irq;
- }
- pd->users++;
- mutex_unlock(&pd->mux);
- return 0; /* success */
-err_request_irq:
- kfree(pd->q);
- pd->q = NULL;
-err_alloc_queue:
- mutex_unlock(&pd->mux);
- return ret;
-}
-
-static int
-dt3155_release(struct file *filp)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- mutex_lock(&pd->mux);
- pd->users--;
- BUG_ON(pd->users < 0);
- if (!pd->users) {
- vb2_queue_release(pd->q);
- free_irq(pd->pdev->irq, pd);
- if (pd->q->alloc_ctx[0])
- vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]);
- kfree(pd->q);
- pd->q = NULL;
- }
- mutex_unlock(&pd->mux);
- return 0;
-}
-
-static ssize_t
-dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
- ssize_t res;
-
- if (mutex_lock_interruptible(&pd->mux))
- return -ERESTARTSYS;
- res = vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
- mutex_unlock(&pd->mux);
- return res;
-}
-
-static unsigned int
-dt3155_poll(struct file *filp, struct poll_table_struct *polltbl)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
- unsigned int res;
-
- mutex_lock(&pd->mux);
- res = vb2_poll(pd->q, filp, polltbl);
- mutex_unlock(&pd->mux);
- return res;
-}
-
-static int
-dt3155_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
- int res;
-
- if (mutex_lock_interruptible(&pd->mux))
- return -ERESTARTSYS;
- res = vb2_mmap(pd->q, vma);
- mutex_unlock(&pd->mux);
- return res;
-}
-
-static const struct v4l2_file_operations dt3155_fops = {
- .owner = THIS_MODULE,
- .open = dt3155_open,
- .release = dt3155_release,
- .read = dt3155_read,
- .poll = dt3155_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = dt3155_mmap,
-};
-
-static int
-dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_streamon(pd->q, type);
-}
-
-static int
-dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_streamoff(pd->q, type);
-}
-
-static int
-dt3155_ioc_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 |
- DT3155_CAPTURE_METHOD;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int
-dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f)
-{
- if (f->index >= NUM_OF_FORMATS)
- return -EINVAL;
- *f = frame_std[f->index];
- return 0;
-}
-
-static int
-dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- f->fmt.pix.width = img_width;
- f->fmt.pix.height = img_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 = 0;
- f->fmt.pix.priv = 0;
- return 0;
-}
-
-static int
-dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (f->fmt.pix.width == img_width &&
- f->fmt.pix.height == img_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)
- return 0;
- else
- return -EINVAL;
-}
-
-static int
-dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
-{
- return dt3155_ioc_g_fmt_vid_cap(filp, p, f);
-}
-
-static int
-dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_reqbufs(pd->q, b);
-}
-
-static int
-dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_querybuf(pd->q, b);
-}
-
-static int
-dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_qbuf(pd->q, b);
-}
-
-static int
-dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b)
-{
- struct dt3155_priv *pd = video_drvdata(filp);
-
- return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK);
-}
-
-static int
-dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm)
-{
- *norm = DT3155_CURRENT_NORM;
- return 0;
-}
-
-static int
-dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm)
-{
- *norm = DT3155_CURRENT_NORM;
- return 0;
-}
-
-static int
-dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id norm)
-{
- if (norm & DT3155_CURRENT_NORM)
- return 0;
- return -EINVAL;
-}
-
-static int
-dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input)
-{
- if (input->index)
- return -EINVAL;
- strcpy(input->name, "Coax in");
- input->type = V4L2_INPUT_TYPE_CAMERA;
- /*
- * FIXME: input->std = 0 according to v4l2 API
- * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD
- * should return -EINVAL
- */
- input->std = DT3155_CURRENT_NORM;
- input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */
- return 0;
-}
-
-static int
-dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int
-dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i)
-{
- if (i)
- return -EINVAL;
- return 0;
-}
-
-static int
-dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
-{
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parms->parm.capture.capturemode = 0;
- parms->parm.capture.timeperframe.numerator = 1001;
- parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
- parms->parm.capture.extendedmode = 0;
- parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
- return 0;
-}
-
-static int
-dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
-{
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parms->parm.capture.capturemode = 0;
- parms->parm.capture.timeperframe.numerator = 1001;
- parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
- parms->parm.capture.extendedmode = 0;
- parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
- return 0;
-}
-
-static const struct v4l2_ioctl_ops dt3155_ioctl_ops = {
- .vidioc_streamon = dt3155_ioc_streamon,
- .vidioc_streamoff = dt3155_ioc_streamoff,
- .vidioc_querycap = dt3155_ioc_querycap,
-/*
- .vidioc_g_priority = dt3155_ioc_g_priority,
- .vidioc_s_priority = dt3155_ioc_s_priority,
-*/
- .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap,
- .vidioc_reqbufs = dt3155_ioc_reqbufs,
- .vidioc_querybuf = dt3155_ioc_querybuf,
- .vidioc_qbuf = dt3155_ioc_qbuf,
- .vidioc_dqbuf = dt3155_ioc_dqbuf,
- .vidioc_querystd = dt3155_ioc_querystd,
- .vidioc_g_std = dt3155_ioc_g_std,
- .vidioc_s_std = dt3155_ioc_s_std,
- .vidioc_enum_input = dt3155_ioc_enum_input,
- .vidioc_g_input = dt3155_ioc_g_input,
- .vidioc_s_input = dt3155_ioc_s_input,
-/*
- .vidioc_queryctrl = dt3155_ioc_queryctrl,
- .vidioc_g_ctrl = dt3155_ioc_g_ctrl,
- .vidioc_s_ctrl = dt3155_ioc_s_ctrl,
- .vidioc_querymenu = dt3155_ioc_querymenu,
- .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls,
-*/
- .vidioc_g_parm = dt3155_ioc_g_parm,
- .vidioc_s_parm = dt3155_ioc_s_parm,
-/*
- .vidioc_cropcap = dt3155_ioc_cropcap,
- .vidioc_g_crop = dt3155_ioc_g_crop,
- .vidioc_s_crop = dt3155_ioc_s_crop,
- .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes,
- .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals,
-*/
-};
-
-static int
-dt3155_init_board(struct pci_dev *pdev)
-{
- struct dt3155_priv *pd = pci_get_drvdata(pdev);
- void *buf_cpu;
- dma_addr_t buf_dma;
- int i;
- u8 tmp;
-
- pci_set_master(pdev); /* dt3155 needs it */
-
- /* resetting the adapter */
- iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN,
- pd->regs + CSR1);
- mmiowb();
- msleep(20);
-
- /* initializing adaper 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);
-
- /* allocate memory, and initialize the DMA machine */
- buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma,
- GFP_KERNEL);
- if (!buf_cpu)
- return -ENOMEM;
- iowrite32(buf_dma, pd->regs + EVEN_DMA_START);
- iowrite32(buf_dma, pd->regs + ODD_DMA_START);
- iowrite32(0, pd->regs + EVEN_DMA_STRIDE);
- iowrite32(0, pd->regs + ODD_DMA_STRIDE);
-
- /* Perform a pseudo even field acquire */
- iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1);
- write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL);
- write_i2c_reg(pd->regs, CONFIG, pd->config);
- write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL);
- write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL);
- msleep(100);
- read_i2c_reg(pd->regs, CSR2, &tmp);
- write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
- write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
- write_i2c_reg(pd->regs, CSR2, pd->csr2);
- iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1);
-
- /* deallocate memory */
- dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma);
- if (tmp & BUSY_EVEN)
- return -EIO;
- 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 = DT3155_CURRENT_NORM,
-};
-
-/* same as in drivers/base/dma-coherent.c */
-struct dma_coherent_mem {
- void *virt_base;
- dma_addr_t device_base;
- int size;
- int flags;
- unsigned long *bitmap;
-};
-
-static int
-dt3155_alloc_coherent(struct device *dev, size_t size, int flags)
-{
- struct dma_coherent_mem *mem;
- dma_addr_t dev_base;
- int pages = size >> PAGE_SHIFT;
- int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
-
- if ((flags & DMA_MEMORY_MAP) == 0)
- goto out;
- if (!size)
- goto out;
- if (dev->dma_mem)
- goto out;
-
- mem = kzalloc(sizeof(*mem), GFP_KERNEL);
- if (!mem)
- goto out;
- mem->virt_base = dma_alloc_coherent(dev, size, &dev_base,
- DT3155_COH_FLAGS);
- if (!mem->virt_base)
- goto err_alloc_coherent;
- mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
- if (!mem->bitmap)
- goto err_bitmap;
-
- /* coherent_dma_mask is already set to 32 bits */
- mem->device_base = dev_base;
- mem->size = pages;
- mem->flags = flags;
- dev->dma_mem = mem;
- return DMA_MEMORY_MAP;
-
-err_bitmap:
- dma_free_coherent(dev, size, mem->virt_base, dev_base);
-err_alloc_coherent:
- kfree(mem);
-out:
- return 0;
-}
-
-static void
-dt3155_free_coherent(struct device *dev)
-{
- struct dma_coherent_mem *mem = dev->dma_mem;
-
- if (!mem)
- return;
- dev->dma_mem = NULL;
- dma_free_coherent(dev, mem->size << PAGE_SHIFT,
- mem->virt_base, mem->device_base);
- kfree(mem->bitmap);
- kfree(mem);
-}
-
-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;
-
- pd->vdev = dt3155_vdev;
- pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */
- video_set_drvdata(&pd->vdev, pd); /* for use in video_fops */
- pd->users = 0;
- pd->pdev = pdev;
- INIT_LIST_HEAD(&pd->dmaq);
- mutex_init(&pd->mux);
- pd->vdev.lock = &pd->mux; /* for locking v4l2_file_operations */
- spin_lock_init(&pd->lock);
- pd->csr2 = csr2_init;
- pd->config = config_init;
- err = pci_enable_device(pdev);
- if (err)
- return err;
- err = pci_request_region(pdev, 0, pci_name(pdev));
- if (err)
- goto err_req_region;
- pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0));
- if (!pd->regs) {
- err = -ENOMEM;
- goto err_pci_iomap;
- }
- err = dt3155_init_board(pdev);
- if (err)
- goto err_init_board;
- err = video_register_device(&pd->vdev, VFL_TYPE_GRABBER, -1);
- if (err)
- goto err_init_board;
- if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE,
- DMA_MEMORY_MAP))
- dev_info(&pdev->dev, "preallocated 8 buffers\n");
- dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor);
- return 0; /* success */
-
-err_init_board:
- pci_iounmap(pdev, pd->regs);
-err_pci_iomap:
- pci_release_region(pdev, 0);
-err_req_region:
- pci_disable_device(pdev);
- return err;
-}
-
-static void
-dt3155_remove(struct pci_dev *pdev)
-{
- struct dt3155_priv *pd = pci_get_drvdata(pdev);
-
- dt3155_free_coherent(&pdev->dev);
- video_unregister_device(&pd->vdev);
- pci_iounmap(pdev, pd->regs);
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
-}
-
-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 <mitov@issp.bas.bg>");
-MODULE_VERSION(DT3155_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
index 335b98a..62ec9f7 100644
--- a/drivers/staging/media/lirc/lirc_imon.c
+++ b/drivers/staging/media/lirc/lirc_imon.c
@@ -693,10 +693,9 @@ static int imon_probe(struct usb_interface *interface,
int ifnum;
int lirc_minor = 0;
int num_endpts;
- int retval = 0;
+ int retval = -ENOMEM;
int display_ep_found = 0;
int ir_ep_found = 0;
- int alloc_status = 0;
int vfd_proto_6p = 0;
struct imon_context *context = NULL;
int i;
@@ -706,10 +705,8 @@ static int imon_probe(struct usb_interface *interface,
mutex_lock(&driver_lock);
context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
- if (!context) {
- alloc_status = 1;
- goto alloc_status_switch;
- }
+ if (!context)
+ goto driver_unlock;
/*
* Try to auto-detect the type of display if the user hasn't set
@@ -775,8 +772,7 @@ static int imon_probe(struct usb_interface *interface,
dev_err(dev, "%s: no valid input (IR) endpoint found.\n",
__func__);
retval = -ENODEV;
- alloc_status = 2;
- goto alloc_status_switch;
+ goto free_context;
}
/* Determine if display requires 6 packets */
@@ -790,31 +786,26 @@ static int imon_probe(struct usb_interface *interface,
driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
if (!driver) {
- alloc_status = 2;
- goto alloc_status_switch;
+ goto free_context;
}
rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!rbuf) {
- alloc_status = 3;
- goto alloc_status_switch;
+ goto free_driver;
}
if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
dev_err(dev, "%s: lirc_buffer_init failed\n", __func__);
- alloc_status = 4;
- goto alloc_status_switch;
+ goto free_rbuf;
}
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!rx_urb) {
dev_err(dev, "%s: usb_alloc_urb failed for IR urb\n", __func__);
- alloc_status = 5;
- goto alloc_status_switch;
+ goto free_lirc_buf;
}
tx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!tx_urb) {
dev_err(dev, "%s: usb_alloc_urb failed for display urb\n",
__func__);
- alloc_status = 6;
- goto alloc_status_switch;
+ goto free_rx_urb;
}
mutex_init(&context->ctx_lock);
@@ -840,11 +831,11 @@ static int imon_probe(struct usb_interface *interface,
lirc_minor = lirc_register_driver(driver);
if (lirc_minor < 0) {
dev_err(dev, "%s: lirc_register_driver failed\n", __func__);
- alloc_status = 7;
- goto unlock;
- } else
- dev_info(dev, "Registered iMON driver (lirc minor: %d)\n",
- lirc_minor);
+ goto free_tx_urb;
+ }
+
+ dev_info(dev, "Registered iMON driver (lirc minor: %d)\n",
+ lirc_minor);
/* Needed while unregistering! */
driver->minor = lirc_minor;
@@ -872,11 +863,9 @@ static int imon_probe(struct usb_interface *interface,
context->rx_endpoint->bInterval);
retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
-
if (retval) {
dev_err(dev, "usb_submit_urb failed for intf0 (%d)\n", retval);
- alloc_status = 8;
- goto unlock;
+ goto unregister_lirc;
}
usb_set_intfdata(interface, context);
@@ -895,39 +884,31 @@ static int imon_probe(struct usb_interface *interface,
dev_info(dev, "iMON device (%04x:%04x, intf%d) on usb<%d:%d> initialized\n",
vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum);
-unlock:
- mutex_unlock(&context->ctx_lock);
-alloc_status_switch:
-
- switch (alloc_status) {
- case 8:
- lirc_unregister_driver(driver->minor);
- case 7:
- usb_free_urb(tx_urb);
- case 6:
- usb_free_urb(rx_urb);
- /* fall-through */
- case 5:
- if (rbuf)
- lirc_buffer_free(rbuf);
- /* fall-through */
- case 4:
- kfree(rbuf);
- /* fall-through */
- case 3:
- kfree(driver);
- /* fall-through */
- case 2:
- kfree(context);
- context = NULL;
- case 1:
- if (retval != -ENODEV)
- retval = -ENOMEM;
- break;
- case 0:
- retval = 0;
- }
+ /* Everything went fine. Just unlock and return retval (with is 0) */
+ goto driver_unlock;
+
+unregister_lirc:
+ lirc_unregister_driver(driver->minor);
+
+free_tx_urb:
+ usb_free_urb(tx_urb);
+
+free_rx_urb:
+ usb_free_urb(rx_urb);
+
+free_lirc_buf:
+ lirc_buffer_free(rbuf);
+
+free_rbuf:
+ kfree(rbuf);
+
+free_driver:
+ kfree(driver);
+free_context:
+ kfree(context);
+ context = NULL;
+driver_unlock:
mutex_unlock(&driver_lock);
return retval;
diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index 29087f6..4f326e9 100644
--- a/drivers/staging/media/lirc/lirc_sir.c
+++ b/drivers/staging/media/lirc/lirc_sir.c
@@ -44,7 +44,7 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/serial_reg.h>
-#include <linux/time.h>
+#include <linux/ktime.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wait.h>
@@ -127,9 +127,9 @@ static int threshold = 3;
static DEFINE_SPINLOCK(timer_lock);
static struct timer_list timerlist;
/* time of last signal change detected */
-static struct timeval last_tv = {0, 0};
+static ktime_t last;
/* time of last UART data ready interrupt */
-static struct timeval last_intr_tv = {0, 0};
+static ktime_t last_intr_time;
static int last_value;
static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
@@ -400,20 +400,6 @@ static void drop_chrdev(void)
}
/* SECTION: Hardware */
-static long delta(struct timeval *tv1, struct timeval *tv2)
-{
- unsigned long deltv;
-
- deltv = tv2->tv_sec - tv1->tv_sec;
- if (deltv > 15)
- deltv = 0xFFFFFF;
- else
- deltv = deltv*1000000 +
- tv2->tv_usec -
- tv1->tv_usec;
- return deltv;
-}
-
static void sir_timeout(unsigned long data)
{
/*
@@ -432,12 +418,14 @@ static void sir_timeout(unsigned long data)
/* clear unread bits in UART and restart */
outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
/* determine 'virtual' pulse end: */
- pulse_end = delta(&last_tv, &last_intr_tv);
+ pulse_end = min_t(unsigned long,
+ ktime_us_delta(last, last_intr_time),
+ PULSE_MASK);
dev_dbg(driver.dev, "timeout add %d for %lu usec\n",
last_value, pulse_end);
add_read_queue(last_value, pulse_end);
last_value = 0;
- last_tv = last_intr_tv;
+ last = last_intr_time;
}
spin_unlock_irqrestore(&timer_lock, flags);
}
@@ -445,9 +433,9 @@ static void sir_timeout(unsigned long data)
static irqreturn_t sir_interrupt(int irq, void *dev_id)
{
unsigned char data;
- struct timeval curr_tv;
- static unsigned long deltv;
- unsigned long deltintrtv;
+ ktime_t curr_time;
+ static unsigned long delt;
+ unsigned long deltintr;
unsigned long flags;
int iir, lsr;
@@ -471,49 +459,46 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id)
do {
del_timer(&timerlist);
data = inb(io + UART_RX);
- do_gettimeofday(&curr_tv);
- deltv = delta(&last_tv, &curr_tv);
- deltintrtv = delta(&last_intr_tv, &curr_tv);
+ curr_time = ktime_get();
+ delt = min_t(unsigned long,
+ ktime_us_delta(last, curr_time),
+ PULSE_MASK);
+ deltintr = min_t(unsigned long,
+ ktime_us_delta(last_intr_time,
+ curr_time),
+ PULSE_MASK);
dev_dbg(driver.dev, "t %lu, d %d\n",
- deltintrtv, (int)data);
+ deltintr, (int)data);
/*
* if nothing came in last X cycles,
* it was gap
*/
- if (deltintrtv > TIME_CONST * threshold) {
+ if (deltintr > TIME_CONST * threshold) {
if (last_value) {
dev_dbg(driver.dev, "GAP\n");
/* simulate signal change */
add_read_queue(last_value,
- deltv -
- deltintrtv);
+ delt -
+ deltintr);
last_value = 0;
- last_tv.tv_sec =
- last_intr_tv.tv_sec;
- last_tv.tv_usec =
- last_intr_tv.tv_usec;
- deltv = deltintrtv;
+ last = last_intr_time;
+ delt = deltintr;
}
}
data = 1;
if (data ^ last_value) {
/*
- * deltintrtv > 2*TIME_CONST, remember?
+ * deltintr > 2*TIME_CONST, remember?
* the other case is timeout
*/
add_read_queue(last_value,
- deltv-TIME_CONST);
+ delt-TIME_CONST);
last_value = data;
- last_tv = curr_tv;
- if (last_tv.tv_usec >= TIME_CONST) {
- last_tv.tv_usec -= TIME_CONST;
- } else {
- last_tv.tv_sec--;
- last_tv.tv_usec += 1000000 -
- TIME_CONST;
- }
+ last = curr_time;
+ last = ktime_sub_us(last,
+ TIME_CONST);
}
- last_intr_tv = curr_tv;
+ last_intr_time = curr_time;
if (data) {
/*
* start timer for end of
diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c
index a4cfcf5..a8d45f4 100644
--- a/drivers/staging/media/mn88472/mn88472.c
+++ b/drivers/staging/media/mn88472/mn88472.c
@@ -218,7 +218,7 @@ err:
return ret;
}
-static int mn88472_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88472_dev *dev = i2c_get_clientdata(client);
@@ -344,12 +344,12 @@ static int mn88472_init(struct dvb_frontend *fe)
if (ret) {
dev_err(&client->dev,
"parity reg read failed=%d\n", ret);
- goto err;
+ goto firmware_release;
}
if (tmp & 0x10) {
dev_err(&client->dev,
"firmware parity check failed=0x%x\n", tmp);
- goto err;
+ goto firmware_release;
}
dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h
index 9ba8c8b..1a0de9e 100644
--- a/drivers/staging/media/mn88472/mn88472_priv.h
+++ b/drivers/staging/media/mn88472/mn88472_priv.h
@@ -29,7 +29,7 @@ struct mn88472_dev {
struct regmap *regmap[3];
struct dvb_frontend fe;
u16 i2c_wr_max;
- fe_delivery_system_t delivery_system;
+ enum fe_delivery_system delivery_system;
bool warm; /* FW running */
u32 xtal;
int ts_mode;
diff --git a/drivers/staging/media/mn88473/mn88473.c b/drivers/staging/media/mn88473/mn88473.c
index 8b6736c..f9146a1 100644
--- a/drivers/staging/media/mn88473/mn88473.c
+++ b/drivers/staging/media/mn88473/mn88473.c
@@ -167,7 +167,7 @@ err:
return ret;
}
-static int mn88473_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
struct mn88473_dev *dev = i2c_get_clientdata(client);
diff --git a/drivers/staging/media/mn88473/mn88473_priv.h b/drivers/staging/media/mn88473/mn88473_priv.h
index ef6f013..54beb42 100644
--- a/drivers/staging/media/mn88473/mn88473_priv.h
+++ b/drivers/staging/media/mn88473/mn88473_priv.h
@@ -29,7 +29,7 @@ struct mn88473_dev {
struct regmap *regmap[3];
struct dvb_frontend fe;
u16 i2c_wr_max;
- fe_delivery_system_t delivery_system;
+ enum fe_delivery_system delivery_system;
bool warm; /* FW running */
u32 xtal;
};
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
index 7ced940..9bfb725 100644
--- a/drivers/staging/media/omap4iss/iss.c
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -1489,7 +1489,7 @@ static int iss_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id omap4iss_id_table[] = {
+static const struct platform_device_id omap4iss_id_table[] = {
{ "omap4iss", 0 },
{ },
};
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
index d7ff769..bc83f82 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.c
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -828,8 +828,10 @@ static const struct iss_video_operations csi2_issvideo_ops = {
*/
static struct v4l2_mbus_framefmt *
-__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, enum v4l2_subdev_format_whence which)
+__csi2_get_format(struct iss_csi2_device *csi2,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad);
@@ -838,8 +840,10 @@ __csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *c
}
static void
-csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+csi2_try_format(struct iss_csi2_device *csi2,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
u32 pixelcode;
@@ -967,7 +971,8 @@ static int csi2_enum_frame_size(struct v4l2_subdev *sd,
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
-static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+static int csi2_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
@@ -988,7 +993,8 @@ static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config
* @fmt: pointer to v4l2 subdev format structure
* return -EINVAL or zero on success
*/
-static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+static int csi2_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c
index eaa82da..f94a592 100644
--- a/drivers/staging/media/omap4iss/iss_ipipe.c
+++ b/drivers/staging/media/omap4iss/iss_ipipe.c
@@ -24,8 +24,10 @@
#include "iss_ipipe.h"
static struct v4l2_mbus_framefmt *
-__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, enum v4l2_subdev_format_whence which);
+__ipipe_get_format(struct iss_ipipe_device *ipipe,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which);
static const unsigned int ipipe_fmts[] = {
MEDIA_BUS_FMT_SGRBG10_1X10,
@@ -176,8 +178,10 @@ static int ipipe_set_stream(struct v4l2_subdev *sd, int enable)
}
static struct v4l2_mbus_framefmt *
-__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, enum v4l2_subdev_format_whence which)
+__ipipe_get_format(struct iss_ipipe_device *ipipe,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad);
@@ -193,9 +197,11 @@ __ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config
* @fmt: Format
*/
static void
-ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
- unsigned int pad, struct v4l2_mbus_framefmt *fmt,
- enum v4l2_subdev_format_whence which)
+ipipe_try_format(struct iss_ipipe_device *ipipe,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
unsigned int width = fmt->width;
@@ -306,8 +312,9 @@ static int ipipe_enum_frame_size(struct v4l2_subdev *sd,
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int ipipe_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
@@ -329,8 +336,9 @@ static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_confi
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int ipipe_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c
index 530ac84..c0da13d 100644
--- a/drivers/staging/media/omap4iss/iss_ipipeif.c
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.c
@@ -518,8 +518,9 @@ static int ipipeif_enum_frame_size(struct v4l2_subdev *sd,
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int ipipeif_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
@@ -541,8 +542,9 @@ static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_con
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int ipipeif_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c
index 5f69012..5030cf3 100644
--- a/drivers/staging/media/omap4iss/iss_resizer.c
+++ b/drivers/staging/media/omap4iss/iss_resizer.c
@@ -580,8 +580,9 @@ static int resizer_enum_frame_size(struct v4l2_subdev *sd,
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *fmt)
+static int resizer_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
{
struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
@@ -603,7 +604,8 @@ static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_con
* Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
* to the format type.
*/
-static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+static int resizer_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index af40db0..118938e 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -42,6 +42,17 @@ config THERMAL_OF
Say 'Y' here if you need to build thermal infrastructure
based on device tree.
+config THERMAL_WRITABLE_TRIPS
+ bool "Enable writable trip points"
+ help
+ This option allows the system integrator to choose whether
+ trip temperatures can be changed from userspace. The
+ writable trips need to be specified when setting up the
+ thermal zone but the choice here takes precedence.
+
+ Say 'Y' here if you would like to allow userspace tools to
+ change trip temperatures.
+
choice
prompt "Default Thermal governor"
default THERMAL_DEFAULT_GOV_STEP_WISE
@@ -71,6 +82,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE
Select this if you want to let the user space manage the
platform thermals.
+config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
+ bool "power_allocator"
+ select THERMAL_GOV_POWER_ALLOCATOR
+ help
+ Select this if you want to control temperature based on
+ system and device power allocation. This governor can only
+ operate on cooling devices that implement the power API.
+
endchoice
config THERMAL_GOV_FAIR_SHARE
@@ -99,6 +118,12 @@ config THERMAL_GOV_USER_SPACE
help
Enable this to let the user space manage the platform thermals.
+config THERMAL_GOV_POWER_ALLOCATOR
+ bool "Power allocator thermal governor"
+ help
+ Enable this to manage platform thermals by dynamically
+ allocating and limiting power to devices.
+
config CPU_THERMAL
bool "generic cpu cooling support"
depends on CPU_FREQ
@@ -136,6 +161,14 @@ config THERMAL_EMULATION
because userland can easily disable the thermal policy by simply
flooding this sysfs node with low temperature values.
+config HISI_THERMAL
+ tristate "Hisilicon thermal driver"
+ depends on ARCH_HISI && CPU_THERMAL && OF
+ help
+ Enable this to plug hisilicon's thermal sensor driver into the Linux
+ thermal framework. cpufreq is used as the cooling device to throttle
+ CPUs when the passive trip is crossed.
+
config IMX_THERMAL
tristate "Temperature sensor driver for Freescale i.MX SoCs"
depends on CPU_THERMAL
@@ -249,9 +282,20 @@ config X86_PKG_TEMP_THERMAL
two trip points which can be set by user to get notifications via thermal
notification methods.
+config INTEL_SOC_DTS_IOSF_CORE
+ tristate
+ depends on X86
+ select IOSF_MBI
+ help
+ This is becoming a common feature for Intel SoCs to expose the additional
+ digital temperature sensors (DTSs) using side band interface (IOSF). This
+ implements the common set of helper functions to register, get temperature
+ and get/set thresholds on DTSs.
+
config INTEL_SOC_DTS_THERMAL
tristate "Intel SoCs DTS thermal driver"
- depends on X86 && IOSF_MBI
+ depends on X86
+ select INTEL_SOC_DTS_IOSF_CORE
help
Enable this to register Intel SoCs (e.g. Bay Trail) platform digital
temperature sensor (DTS). These SoCs have two additional DTSs in
@@ -261,12 +305,23 @@ config INTEL_SOC_DTS_THERMAL
notification methods.The other trip is a critical trip point, which
was set by the driver based on the TJ MAX temperature.
+config INTEL_QUARK_DTS_THERMAL
+ tristate "Intel Quark DTS thermal driver"
+ depends on X86_INTEL_QUARK
+ help
+ Enable this to register Intel Quark SoC (e.g. X1000) platform digital
+ temperature sensor (DTS). For X1000 SoC, it has one on-die DTS.
+ The DTS will be registered as a thermal zone. There are two trip points:
+ hot & critical. The critical trip point default value is set by
+ underlying BIOS/Firmware.
+
config INT340X_THERMAL
tristate "ACPI INT340X thermal drivers"
depends on X86 && ACPI
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
+ select INTEL_SOC_DTS_IOSF_CORE
help
Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core
@@ -299,4 +354,15 @@ depends on ARCH_STI && OF
source "drivers/thermal/st/Kconfig"
endmenu
+config QCOM_SPMI_TEMP_ALARM
+ tristate "Qualcomm SPMI PMIC Temperature Alarm"
+ depends on OF && SPMI && IIO
+ select REGMAP_SPMI
+ help
+ This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
+ PMIC devices. It shows up in sysfs as a thermal sensor with multiple
+ trip points. The temperature reported by the thermal sensor reflects the
+ real time die temperature if an ADC is present or an estimate of the
+ temperature based upon the over temperature stage value.
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index fa0dc48..535dfee 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -14,6 +14,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
+thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o
# cpufreq cooling
thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
@@ -22,6 +23,7 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
# platform thermal drivers
+obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
@@ -34,8 +36,11 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
+obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
+obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_ST_THERMAL) += st/
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
+obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index f65f0d1..6509c61 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -26,10 +26,13 @@
#include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
+#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/cpu_cooling.h>
+#include <trace/events/thermal.h>
+
/*
* Cooling state <-> CPUFreq frequency
*
@@ -45,6 +48,19 @@
*/
/**
+ * struct power_table - frequency to power conversion
+ * @frequency: frequency in KHz
+ * @power: power in mW
+ *
+ * This structure is built when the cooling device registers and helps
+ * in translating frequency to power and viceversa.
+ */
+struct power_table {
+ u32 frequency;
+ u32 power;
+};
+
+/**
* struct cpufreq_cooling_device - data for cooling device with cpufreq
* @id: unique integer value corresponding to each cpufreq_cooling_device
* registered.
@@ -58,6 +74,15 @@
* cpufreq frequencies.
* @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
* @node: list_head to link all cpufreq_cooling_device together.
+ * @last_load: load measured by the latest call to cpufreq_get_actual_power()
+ * @time_in_idle: previous reading of the absolute time that this cpu was idle
+ * @time_in_idle_timestamp: wall time of the last invocation of
+ * get_cpu_idle_time_us()
+ * @dyn_power_table: array of struct power_table for frequency to power
+ * conversion, sorted in ascending order.
+ * @dyn_power_table_entries: number of entries in the @dyn_power_table array
+ * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs registered
+ * @plat_get_static_power: callback to calculate the static power
*
* This structure is required for keeping information of each registered
* cpufreq_cooling_device.
@@ -71,6 +96,13 @@ struct cpufreq_cooling_device {
unsigned int *freq_table; /* In descending order */
struct cpumask allowed_cpus;
struct list_head node;
+ u32 last_load;
+ u64 *time_in_idle;
+ u64 *time_in_idle_timestamp;
+ struct power_table *dyn_power_table;
+ int dyn_power_table_entries;
+ struct device *cpu_dev;
+ get_static_t plat_get_static_power;
};
static DEFINE_IDR(cpufreq_idr);
static DEFINE_MUTEX(cooling_cpufreq_lock);
@@ -186,23 +218,237 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
unsigned long max_freq = 0;
struct cpufreq_cooling_device *cpufreq_dev;
- if (event != CPUFREQ_ADJUST)
- return 0;
+ switch (event) {
- mutex_lock(&cooling_cpufreq_lock);
- list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
- if (!cpumask_test_cpu(policy->cpu,
- &cpufreq_dev->allowed_cpus))
+ case CPUFREQ_ADJUST:
+ mutex_lock(&cooling_cpufreq_lock);
+ list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+ if (!cpumask_test_cpu(policy->cpu,
+ &cpufreq_dev->allowed_cpus))
+ continue;
+
+ max_freq = cpufreq_dev->cpufreq_val;
+
+ if (policy->max != max_freq)
+ cpufreq_verify_within_limits(policy, 0,
+ max_freq);
+ }
+ mutex_unlock(&cooling_cpufreq_lock);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+/**
+ * build_dyn_power_table() - create a dynamic power to frequency table
+ * @cpufreq_device: the cpufreq cooling device in which to store the table
+ * @capacitance: dynamic power coefficient for these cpus
+ *
+ * Build a dynamic power to frequency table for this cpu and store it
+ * in @cpufreq_device. This table will be used in cpu_power_to_freq() and
+ * cpu_freq_to_power() to convert between power and frequency
+ * efficiently. Power is stored in mW, frequency in KHz. The
+ * resulting table is in ascending order.
+ *
+ * Return: 0 on success, -E* on error.
+ */
+static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
+ u32 capacitance)
+{
+ struct power_table *power_table;
+ struct dev_pm_opp *opp;
+ struct device *dev = NULL;
+ int num_opps = 0, cpu, i, ret = 0;
+ unsigned long freq;
+
+ rcu_read_lock();
+
+ for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+ dev = get_cpu_device(cpu);
+ if (!dev) {
+ dev_warn(&cpufreq_device->cool_dev->device,
+ "No cpu device for cpu %d\n", cpu);
continue;
+ }
+
+ num_opps = dev_pm_opp_get_opp_count(dev);
+ if (num_opps > 0) {
+ break;
+ } else if (num_opps < 0) {
+ ret = num_opps;
+ goto unlock;
+ }
+ }
- max_freq = cpufreq_dev->cpufreq_val;
+ if (num_opps == 0) {
+ ret = -EINVAL;
+ goto unlock;
+ }
- if (policy->max != max_freq)
- cpufreq_verify_within_limits(policy, 0, max_freq);
+ power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
+ if (!power_table) {
+ ret = -ENOMEM;
+ goto unlock;
}
- mutex_unlock(&cooling_cpufreq_lock);
- return 0;
+ for (freq = 0, i = 0;
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
+ freq++, i++) {
+ u32 freq_mhz, voltage_mv;
+ u64 power;
+
+ freq_mhz = freq / 1000000;
+ voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
+
+ /*
+ * Do the multiplication with MHz and millivolt so as
+ * to not overflow.
+ */
+ power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
+ do_div(power, 1000000000);
+
+ /* frequency is stored in power_table in KHz */
+ power_table[i].frequency = freq / 1000;
+
+ /* power is stored in mW */
+ power_table[i].power = power;
+ }
+
+ if (i == 0) {
+ ret = PTR_ERR(opp);
+ goto unlock;
+ }
+
+ cpufreq_device->cpu_dev = dev;
+ cpufreq_device->dyn_power_table = power_table;
+ cpufreq_device->dyn_power_table_entries = i;
+
+unlock:
+ rcu_read_unlock();
+ return ret;
+}
+
+static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,
+ u32 freq)
+{
+ int i;
+ struct power_table *pt = cpufreq_device->dyn_power_table;
+
+ for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+ if (freq < pt[i].frequency)
+ break;
+
+ return pt[i - 1].power;
+}
+
+static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_device,
+ u32 power)
+{
+ int i;
+ struct power_table *pt = cpufreq_device->dyn_power_table;
+
+ for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+ if (power < pt[i].power)
+ break;
+
+ return pt[i - 1].frequency;
+}
+
+/**
+ * get_load() - get load for a cpu since last updated
+ * @cpufreq_device: &struct cpufreq_cooling_device for this cpu
+ * @cpu: cpu number
+ *
+ * Return: The average load of cpu @cpu in percentage since this
+ * function was last called.
+ */
+static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
+{
+ u32 load;
+ u64 now, now_idle, delta_time, delta_idle;
+
+ now_idle = get_cpu_idle_time(cpu, &now, 0);
+ delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
+ delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
+
+ if (delta_time <= delta_idle)
+ load = 0;
+ else
+ load = div64_u64(100 * (delta_time - delta_idle), delta_time);
+
+ cpufreq_device->time_in_idle[cpu] = now_idle;
+ cpufreq_device->time_in_idle_timestamp[cpu] = now;
+
+ return load;
+}
+
+/**
+ * get_static_power() - calculate the static power consumed by the cpus
+ * @cpufreq_device: struct &cpufreq_cooling_device for this cpu cdev
+ * @tz: thermal zone device in which we're operating
+ * @freq: frequency in KHz
+ * @power: pointer in which to store the calculated static power
+ *
+ * Calculate the static power consumed by the cpus described by
+ * @cpu_actor running at frequency @freq. This function relies on a
+ * platform specific function that should have been provided when the
+ * actor was registered. If it wasn't, the static power is assumed to
+ * be negligible. The calculated static power is stored in @power.
+ *
+ * Return: 0 on success, -E* on failure.
+ */
+static int get_static_power(struct cpufreq_cooling_device *cpufreq_device,
+ struct thermal_zone_device *tz, unsigned long freq,
+ u32 *power)
+{
+ struct dev_pm_opp *opp;
+ unsigned long voltage;
+ struct cpumask *cpumask = &cpufreq_device->allowed_cpus;
+ unsigned long freq_hz = freq * 1000;
+
+ if (!cpufreq_device->plat_get_static_power ||
+ !cpufreq_device->cpu_dev) {
+ *power = 0;
+ return 0;
+ }
+
+ rcu_read_lock();
+
+ opp = dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
+ true);
+ voltage = dev_pm_opp_get_voltage(opp);
+
+ rcu_read_unlock();
+
+ if (voltage == 0) {
+ dev_warn_ratelimited(cpufreq_device->cpu_dev,
+ "Failed to get voltage for frequency %lu: %ld\n",
+ freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
+ return -EINVAL;
+ }
+
+ return cpufreq_device->plat_get_static_power(cpumask, tz->passive_delay,
+ voltage, power);
+}
+
+/**
+ * get_dynamic_power() - calculate the dynamic power
+ * @cpufreq_device: &cpufreq_cooling_device for this cdev
+ * @freq: current frequency
+ *
+ * Return: the dynamic power consumed by the cpus described by
+ * @cpufreq_device.
+ */
+static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_device,
+ unsigned long freq)
+{
+ u32 raw_cpu_power;
+
+ raw_cpu_power = cpu_freq_to_power(cpufreq_device, freq);
+ return (raw_cpu_power * cpufreq_device->last_load) / 100;
}
/* cpufreq cooling device callback functions are defined below */
@@ -280,8 +526,205 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
return 0;
}
+/**
+ * cpufreq_get_requested_power() - get the current power
+ * @cdev: &thermal_cooling_device pointer
+ * @tz: a valid thermal zone device pointer
+ * @power: pointer in which to store the resulting power
+ *
+ * Calculate the current power consumption of the cpus in milliwatts
+ * and store it in @power. This function should actually calculate
+ * the requested power, but it's hard to get the frequency that
+ * cpufreq would have assigned if there were no thermal limits.
+ * Instead, we calculate the current power on the assumption that the
+ * immediate future will look like the immediate past.
+ *
+ * We use the current frequency and the average load since this
+ * function was last called. In reality, there could have been
+ * multiple opps since this function was last called and that affects
+ * the load calculation. While it's not perfectly accurate, this
+ * simplification is good enough and works. REVISIT this, as more
+ * complex code may be needed if experiments show that it's not
+ * accurate enough.
+ *
+ * Return: 0 on success, -E* if getting the static power failed.
+ */
+static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz,
+ u32 *power)
+{
+ unsigned long freq;
+ int i = 0, cpu, ret;
+ u32 static_power, dynamic_power, total_load = 0;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+ u32 *load_cpu = NULL;
+
+ cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
+
+ /*
+ * All the CPUs are offline, thus the requested power by
+ * the cdev is 0
+ */
+ if (cpu >= nr_cpu_ids) {
+ *power = 0;
+ return 0;
+ }
+
+ freq = cpufreq_quick_get(cpu);
+
+ if (trace_thermal_power_cpu_get_power_enabled()) {
+ u32 ncpus = cpumask_weight(&cpufreq_device->allowed_cpus);
+
+ load_cpu = devm_kcalloc(&cdev->device, ncpus, sizeof(*load_cpu),
+ GFP_KERNEL);
+ }
+
+ for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+ u32 load;
+
+ if (cpu_online(cpu))
+ load = get_load(cpufreq_device, cpu);
+ else
+ load = 0;
+
+ total_load += load;
+ if (trace_thermal_power_cpu_limit_enabled() && load_cpu)
+ load_cpu[i] = load;
+
+ i++;
+ }
+
+ cpufreq_device->last_load = total_load;
+
+ dynamic_power = get_dynamic_power(cpufreq_device, freq);
+ ret = get_static_power(cpufreq_device, tz, freq, &static_power);
+ if (ret) {
+ if (load_cpu)
+ devm_kfree(&cdev->device, load_cpu);
+ return ret;
+ }
+
+ if (load_cpu) {
+ trace_thermal_power_cpu_get_power(
+ &cpufreq_device->allowed_cpus,
+ freq, load_cpu, i, dynamic_power, static_power);
+
+ devm_kfree(&cdev->device, load_cpu);
+ }
+
+ *power = static_power + dynamic_power;
+ return 0;
+}
+
+/**
+ * cpufreq_state2power() - convert a cpu cdev state to power consumed
+ * @cdev: &thermal_cooling_device pointer
+ * @tz: a valid thermal zone device pointer
+ * @state: cooling device state to be converted
+ * @power: pointer in which to store the resulting power
+ *
+ * Convert cooling device state @state into power consumption in
+ * milliwatts assuming 100% load. Store the calculated power in
+ * @power.
+ *
+ * Return: 0 on success, -EINVAL if the cooling device state could not
+ * be converted into a frequency or other -E* if there was an error
+ * when calculating the static power.
+ */
+static int cpufreq_state2power(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz,
+ unsigned long state, u32 *power)
+{
+ unsigned int freq, num_cpus;
+ cpumask_t cpumask;
+ u32 static_power, dynamic_power;
+ int ret;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+ cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
+ num_cpus = cpumask_weight(&cpumask);
+
+ /* None of our cpus are online, so no power */
+ if (num_cpus == 0) {
+ *power = 0;
+ return 0;
+ }
+
+ freq = cpufreq_device->freq_table[state];
+ if (!freq)
+ return -EINVAL;
+
+ dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
+ ret = get_static_power(cpufreq_device, tz, freq, &static_power);
+ if (ret)
+ return ret;
+
+ *power = static_power + dynamic_power;
+ return 0;
+}
+
+/**
+ * cpufreq_power2state() - convert power to a cooling device state
+ * @cdev: &thermal_cooling_device pointer
+ * @tz: a valid thermal zone device pointer
+ * @power: power in milliwatts to be converted
+ * @state: pointer in which to store the resulting state
+ *
+ * Calculate a cooling device state for the cpus described by @cdev
+ * that would allow them to consume at most @power mW and store it in
+ * @state. Note that this calculation depends on external factors
+ * such as the cpu load or the current static power. Calling this
+ * function with the same power as input can yield different cooling
+ * device states depending on those external factors.
+ *
+ * Return: 0 on success, -ENODEV if no cpus are online or -EINVAL if
+ * the calculated frequency could not be converted to a valid state.
+ * The latter should not happen unless the frequencies available to
+ * cpufreq have changed since the initialization of the cpu cooling
+ * device.
+ */
+static int cpufreq_power2state(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, u32 power,
+ unsigned long *state)
+{
+ unsigned int cpu, cur_freq, target_freq;
+ int ret;
+ s32 dyn_power;
+ u32 last_load, normalised_power, static_power;
+ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+ cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
+
+ /* None of our cpus are online */
+ if (cpu >= nr_cpu_ids)
+ return -ENODEV;
+
+ cur_freq = cpufreq_quick_get(cpu);
+ ret = get_static_power(cpufreq_device, tz, cur_freq, &static_power);
+ if (ret)
+ return ret;
+
+ dyn_power = power - static_power;
+ dyn_power = dyn_power > 0 ? dyn_power : 0;
+ last_load = cpufreq_device->last_load ?: 1;
+ normalised_power = (dyn_power * 100) / last_load;
+ target_freq = cpu_power_to_freq(cpufreq_device, normalised_power);
+
+ *state = cpufreq_cooling_get_level(cpu, target_freq);
+ if (*state == THERMAL_CSTATE_INVALID) {
+ dev_warn_ratelimited(&cdev->device,
+ "Failed to convert %dKHz for cpu %d into a cdev state\n",
+ target_freq, cpu);
+ return -EINVAL;
+ }
+
+ trace_thermal_power_cpu_limit(&cpufreq_device->allowed_cpus,
+ target_freq, *state, power);
+ return 0;
+}
+
/* Bind cpufreq callbacks to thermal cooling device ops */
-static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
+static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
.get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state,
@@ -311,6 +754,9 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
* @np: a valid struct device_node to the cooling device device tree node
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
* Normally this should be same as cpufreq policy->related_cpus.
+ * @capacitance: dynamic power coefficient for these cpus
+ * @plat_static_func: function to calculate the static power consumed by these
+ * cpus (optional)
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
@@ -322,13 +768,14 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
*/
static struct thermal_cooling_device *
__cpufreq_cooling_register(struct device_node *np,
- const struct cpumask *clip_cpus)
+ const struct cpumask *clip_cpus, u32 capacitance,
+ get_static_t plat_static_func)
{
struct thermal_cooling_device *cool_dev;
struct cpufreq_cooling_device *cpufreq_dev;
char dev_name[THERMAL_NAME_LENGTH];
struct cpufreq_frequency_table *pos, *table;
- unsigned int freq, i;
+ unsigned int freq, i, num_cpus;
int ret;
table = cpufreq_frequency_get_table(cpumask_first(clip_cpus));
@@ -341,6 +788,23 @@ __cpufreq_cooling_register(struct device_node *np,
if (!cpufreq_dev)
return ERR_PTR(-ENOMEM);
+ num_cpus = cpumask_weight(clip_cpus);
+ cpufreq_dev->time_in_idle = kcalloc(num_cpus,
+ sizeof(*cpufreq_dev->time_in_idle),
+ GFP_KERNEL);
+ if (!cpufreq_dev->time_in_idle) {
+ cool_dev = ERR_PTR(-ENOMEM);
+ goto free_cdev;
+ }
+
+ cpufreq_dev->time_in_idle_timestamp =
+ kcalloc(num_cpus, sizeof(*cpufreq_dev->time_in_idle_timestamp),
+ GFP_KERNEL);
+ if (!cpufreq_dev->time_in_idle_timestamp) {
+ cool_dev = ERR_PTR(-ENOMEM);
+ goto free_time_in_idle;
+ }
+
/* Find max levels */
cpufreq_for_each_valid_entry(pos, table)
cpufreq_dev->max_level++;
@@ -349,7 +813,7 @@ __cpufreq_cooling_register(struct device_node *np,
cpufreq_dev->max_level, GFP_KERNEL);
if (!cpufreq_dev->freq_table) {
cool_dev = ERR_PTR(-ENOMEM);
- goto free_cdev;
+ goto free_time_in_idle_timestamp;
}
/* max_level is an index, not a counter */
@@ -357,6 +821,20 @@ __cpufreq_cooling_register(struct device_node *np,
cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
+ if (capacitance) {
+ cpufreq_cooling_ops.get_requested_power =
+ cpufreq_get_requested_power;
+ cpufreq_cooling_ops.state2power = cpufreq_state2power;
+ cpufreq_cooling_ops.power2state = cpufreq_power2state;
+ cpufreq_dev->plat_get_static_power = plat_static_func;
+
+ ret = build_dyn_power_table(cpufreq_dev, capacitance);
+ if (ret) {
+ cool_dev = ERR_PTR(ret);
+ goto free_table;
+ }
+ }
+
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) {
cool_dev = ERR_PTR(ret);
@@ -402,6 +880,10 @@ remove_idr:
release_idr(&cpufreq_idr, cpufreq_dev->id);
free_table:
kfree(cpufreq_dev->freq_table);
+free_time_in_idle_timestamp:
+ kfree(cpufreq_dev->time_in_idle_timestamp);
+free_time_in_idle:
+ kfree(cpufreq_dev->time_in_idle);
free_cdev:
kfree(cpufreq_dev);
@@ -422,7 +904,7 @@ free_cdev:
struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus)
{
- return __cpufreq_cooling_register(NULL, clip_cpus);
+ return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
@@ -446,11 +928,78 @@ of_cpufreq_cooling_register(struct device_node *np,
if (!np)
return ERR_PTR(-EINVAL);
- return __cpufreq_cooling_register(np, clip_cpus);
+ return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
}
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
/**
+ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @clip_cpus: cpumask of cpus where the frequency constraints will happen
+ * @capacitance: dynamic power coefficient for these cpus
+ * @plat_static_func: function to calculate the static power consumed by these
+ * cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x". This api can support multiple
+ * instances of cpufreq cooling devices. Using this function, the
+ * cooling device will implement the power extensions by using a
+ * simple cpu power model. The cpus must have registered their OPPs
+ * using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus. If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capacitance,
+ get_static_t plat_static_func)
+{
+ return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
+ plat_static_func);
+}
+EXPORT_SYMBOL(cpufreq_power_cooling_register);
+
+/**
+ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @np: a valid struct device_node to the cooling device device tree node
+ * @clip_cpus: cpumask of cpus where the frequency constraints will happen
+ * @capacitance: dynamic power coefficient for these cpus
+ * @plat_static_func: function to calculate the static power consumed by these
+ * cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x". This api can support multiple
+ * instances of cpufreq cooling devices. Using this API, the cpufreq
+ * cooling device will be linked to the device tree node provided.
+ * Using this function, the cooling device will implement the power
+ * extensions by using a simple cpu power model. The cpus must have
+ * registered their OPPs using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus. If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+ const struct cpumask *clip_cpus,
+ u32 capacitance,
+ get_static_t plat_static_func)
+{
+ if (!np)
+ return ERR_PTR(-EINVAL);
+
+ return __cpufreq_cooling_register(np, clip_cpus, capacitance,
+ plat_static_func);
+}
+EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
+
+/**
* cpufreq_cooling_unregister - function to remove cpufreq cooling device.
* @cdev: thermal cooling device pointer.
*
@@ -475,6 +1024,8 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
release_idr(&cpufreq_idr, cpufreq_dev->id);
+ kfree(cpufreq_dev->time_in_idle_timestamp);
+ kfree(cpufreq_dev->time_in_idle);
kfree(cpufreq_dev->freq_table);
kfree(cpufreq_dev);
}
diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c
index 20adfbe..2fb273c 100644
--- a/drivers/thermal/db8500_thermal.c
+++ b/drivers/thermal/db8500_thermal.c
@@ -76,7 +76,7 @@ static int db8500_cdev_bind(struct thermal_zone_device *thermal,
upper = lower = i > max_state ? max_state : i;
ret = thermal_zone_bind_cooling_device(thermal, i, cdev,
- upper, lower);
+ upper, lower, THERMAL_WEIGHT_DEFAULT);
dev_info(&cdev->device, "%s bind to %d: %d-%s\n", cdev->type,
i, ret, ret ? "fail" : "succeed");
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c
index 6e0a3fb..c2c10bb 100644
--- a/drivers/thermal/fair_share.c
+++ b/drivers/thermal/fair_share.c
@@ -59,17 +59,17 @@ static int get_trip_level(struct thermal_zone_device *tz)
}
static long get_target_state(struct thermal_zone_device *tz,
- struct thermal_cooling_device *cdev, int weight, int level)
+ struct thermal_cooling_device *cdev, int percentage, int level)
{
unsigned long max_state;
cdev->ops->get_max_state(cdev, &max_state);
- return (long)(weight * level * max_state) / (100 * tz->trips);
+ return (long)(percentage * level * max_state) / (100 * tz->trips);
}
/**
- * fair_share_throttle - throttles devices asscciated with the given zone
+ * fair_share_throttle - throttles devices associated with the given zone
* @tz - thermal_zone_device
*
* Throttling Logic: This uses three parameters to calculate the new
@@ -77,7 +77,7 @@ static long get_target_state(struct thermal_zone_device *tz,
*
* Parameters used for Throttling:
* P1. max_state: Maximum throttle state exposed by the cooling device.
- * P2. weight[i]/100:
+ * P2. percentage[i]/100:
* How 'effective' the 'i'th device is, in cooling the given zone.
* P3. cur_trip_level/max_no_of_trips:
* This describes the extent to which the devices should be throttled.
@@ -88,28 +88,33 @@ static long get_target_state(struct thermal_zone_device *tz,
*/
static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
{
- const struct thermal_zone_params *tzp;
- struct thermal_cooling_device *cdev;
struct thermal_instance *instance;
- int i;
+ int total_weight = 0;
+ int total_instance = 0;
int cur_trip_level = get_trip_level(tz);
- if (!tz->tzp || !tz->tzp->tbp)
- return -EINVAL;
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ if (instance->trip != trip)
+ continue;
+
+ total_weight += instance->weight;
+ total_instance++;
+ }
- tzp = tz->tzp;
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ int percentage;
+ struct thermal_cooling_device *cdev = instance->cdev;
- for (i = 0; i < tzp->num_tbps; i++) {
- if (!tzp->tbp[i].cdev)
+ if (instance->trip != trip)
continue;
- cdev = tzp->tbp[i].cdev;
- instance = get_thermal_instance(tz, cdev, trip);
- if (!instance)
- continue;
+ if (!total_weight)
+ percentage = 100 / total_instance;
+ else
+ percentage = (instance->weight * 100) / total_weight;
- instance->target = get_target_state(tz, cdev,
- tzp->tbp[i].weight, cur_trip_level);
+ instance->target = get_target_state(tz, cdev, percentage,
+ cur_trip_level);
instance->cdev->updated = false;
thermal_cdev_update(cdev);
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c
new file mode 100644
index 0000000..d5dd357
--- /dev/null
+++ b/drivers/thermal/hisi_thermal.c
@@ -0,0 +1,421 @@
+/*
+ * Hisilicon thermal sensor driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Copyright (c) 2014-2015 Linaro Limited.
+ *
+ * Xinwei Kong <kong.kongxinwei@hisilicon.com>
+ * Leo Yan <leo.yan@linaro.org>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include "thermal_core.h"
+
+#define TEMP0_TH (0x4)
+#define TEMP0_RST_TH (0x8)
+#define TEMP0_CFG (0xC)
+#define TEMP0_EN (0x10)
+#define TEMP0_INT_EN (0x14)
+#define TEMP0_INT_CLR (0x18)
+#define TEMP0_RST_MSK (0x1C)
+#define TEMP0_VALUE (0x28)
+
+#define HISI_TEMP_BASE (-60)
+#define HISI_TEMP_RESET (100000)
+
+#define HISI_MAX_SENSORS 4
+
+struct hisi_thermal_sensor {
+ struct hisi_thermal_data *thermal;
+ struct thermal_zone_device *tzd;
+
+ long sensor_temp;
+ uint32_t id;
+ uint32_t thres_temp;
+};
+
+struct hisi_thermal_data {
+ struct mutex thermal_lock; /* protects register data */
+ struct platform_device *pdev;
+ struct clk *clk;
+ struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS];
+
+ int irq, irq_bind_sensor;
+ bool irq_enabled;
+
+ void __iomem *regs;
+};
+
+/* in millicelsius */
+static inline int _step_to_temp(int step)
+{
+ /*
+ * Every step equals (1 * 200) / 255 celsius, and finally
+ * need convert to millicelsius.
+ */
+ return (HISI_TEMP_BASE + (step * 200 / 255)) * 1000;
+}
+
+static inline long _temp_to_step(long temp)
+{
+ return ((temp / 1000 - HISI_TEMP_BASE) * 255 / 200);
+}
+
+static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data,
+ struct hisi_thermal_sensor *sensor)
+{
+ long val;
+
+ mutex_lock(&data->thermal_lock);
+
+ /* disable interrupt */
+ writel(0x0, data->regs + TEMP0_INT_EN);
+ writel(0x1, data->regs + TEMP0_INT_CLR);
+
+ /* disable module firstly */
+ writel(0x0, data->regs + TEMP0_EN);
+
+ /* select sensor id */
+ writel((sensor->id << 12), data->regs + TEMP0_CFG);
+
+ /* enable module */
+ writel(0x1, data->regs + TEMP0_EN);
+
+ usleep_range(3000, 5000);
+
+ val = readl(data->regs + TEMP0_VALUE);
+ val = _step_to_temp(val);
+
+ mutex_unlock(&data->thermal_lock);
+
+ return val;
+}
+
+static void hisi_thermal_enable_bind_irq_sensor
+ (struct hisi_thermal_data *data)
+{
+ struct hisi_thermal_sensor *sensor;
+
+ mutex_lock(&data->thermal_lock);
+
+ sensor = &data->sensors[data->irq_bind_sensor];
+
+ /* setting the hdak time */
+ writel(0x0, data->regs + TEMP0_CFG);
+
+ /* disable module firstly */
+ writel(0x0, data->regs + TEMP0_RST_MSK);
+ writel(0x0, data->regs + TEMP0_EN);
+
+ /* select sensor id */
+ writel((sensor->id << 12), data->regs + TEMP0_CFG);
+
+ /* enable for interrupt */
+ writel(_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00,
+ data->regs + TEMP0_TH);
+
+ writel(_temp_to_step(HISI_TEMP_RESET), data->regs + TEMP0_RST_TH);
+
+ /* enable module */
+ writel(0x1, data->regs + TEMP0_RST_MSK);
+ writel(0x1, data->regs + TEMP0_EN);
+
+ writel(0x0, data->regs + TEMP0_INT_CLR);
+ writel(0x1, data->regs + TEMP0_INT_EN);
+
+ usleep_range(3000, 5000);
+
+ mutex_unlock(&data->thermal_lock);
+}
+
+static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
+{
+ mutex_lock(&data->thermal_lock);
+
+ /* disable sensor module */
+ writel(0x0, data->regs + TEMP0_INT_EN);
+ writel(0x0, data->regs + TEMP0_RST_MSK);
+ writel(0x0, data->regs + TEMP0_EN);
+
+ mutex_unlock(&data->thermal_lock);
+}
+
+static int hisi_thermal_get_temp(void *_sensor, long *temp)
+{
+ struct hisi_thermal_sensor *sensor = _sensor;
+ struct hisi_thermal_data *data = sensor->thermal;
+
+ int sensor_id = 0, i;
+ long max_temp = 0;
+
+ *temp = hisi_thermal_get_sensor_temp(data, sensor);
+
+ sensor->sensor_temp = *temp;
+
+ for (i = 0; i < HISI_MAX_SENSORS; i++) {
+ if (data->sensors[i].sensor_temp >= max_temp) {
+ max_temp = data->sensors[i].sensor_temp;
+ sensor_id = i;
+ }
+ }
+
+ mutex_lock(&data->thermal_lock);
+ data->irq_bind_sensor = sensor_id;
+ mutex_unlock(&data->thermal_lock);
+
+ dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%ld, thres=%d\n",
+ sensor->id, data->irq_enabled, *temp, sensor->thres_temp);
+ /*
+ * Bind irq to sensor for two cases:
+ * Reenable alarm IRQ if temperature below threshold;
+ * if irq has been enabled, always set it;
+ */
+ if (data->irq_enabled) {
+ hisi_thermal_enable_bind_irq_sensor(data);
+ return 0;
+ }
+
+ if (max_temp < sensor->thres_temp) {
+ data->irq_enabled = true;
+ hisi_thermal_enable_bind_irq_sensor(data);
+ enable_irq(data->irq);
+ }
+
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
+ .get_temp = hisi_thermal_get_temp,
+};
+
+static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev)
+{
+ struct hisi_thermal_data *data = dev;
+
+ disable_irq_nosync(irq);
+ data->irq_enabled = false;
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
+{
+ struct hisi_thermal_data *data = dev;
+ struct hisi_thermal_sensor *sensor;
+ int i;
+
+ mutex_lock(&data->thermal_lock);
+ sensor = &data->sensors[data->irq_bind_sensor];
+
+ dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n",
+ sensor->thres_temp / 1000);
+ mutex_unlock(&data->thermal_lock);
+
+ for (i = 0; i < HISI_MAX_SENSORS; i++)
+ thermal_zone_device_update(data->sensors[i].tzd);
+
+ return IRQ_HANDLED;
+}
+
+static int hisi_thermal_register_sensor(struct platform_device *pdev,
+ struct hisi_thermal_data *data,
+ struct hisi_thermal_sensor *sensor,
+ int index)
+{
+ int ret, i;
+ const struct thermal_trip *trip;
+
+ sensor->id = index;
+ sensor->thermal = data;
+
+ sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, sensor->id,
+ sensor, &hisi_of_thermal_ops);
+ if (IS_ERR(sensor->tzd)) {
+ ret = PTR_ERR(sensor->tzd);
+ dev_err(&pdev->dev, "failed to register sensor id %d: %d\n",
+ sensor->id, ret);
+ return ret;
+ }
+
+ trip = of_thermal_get_trip_points(sensor->tzd);
+
+ for (i = 0; i < of_thermal_get_ntrips(sensor->tzd); i++) {
+ if (trip[i].type == THERMAL_TRIP_PASSIVE) {
+ sensor->thres_temp = trip[i].temperature;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_hisi_thermal_match[] = {
+ { .compatible = "hisilicon,tsensor" },
+ { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_hisi_thermal_match);
+
+static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
+ bool on)
+{
+ struct thermal_zone_device *tzd = sensor->tzd;
+
+ tzd->ops->set_mode(tzd,
+ on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
+}
+
+static int hisi_thermal_probe(struct platform_device *pdev)
+{
+ struct hisi_thermal_data *data;
+ struct resource *res;
+ int i;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->thermal_lock);
+ data->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->regs)) {
+ dev_err(&pdev->dev, "failed to get io address\n");
+ return PTR_ERR(data->regs);
+ }
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq,
+ hisi_thermal_alarm_irq,
+ hisi_thermal_alarm_irq_thread,
+ 0, "hisi_thermal", data);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ data->clk = devm_clk_get(&pdev->dev, "thermal_clk");
+ if (IS_ERR(data->clk)) {
+ ret = PTR_ERR(data->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get thermal clk: %d\n", ret);
+ return ret;
+ }
+
+ /* enable clock for thermal */
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < HISI_MAX_SENSORS; ++i) {
+ ret = hisi_thermal_register_sensor(pdev, data,
+ &data->sensors[i], i);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register thermal sensor: %d\n", ret);
+ goto err_get_sensor_data;
+ }
+ }
+
+ hisi_thermal_enable_bind_irq_sensor(data);
+ data->irq_enabled = true;
+
+ for (i = 0; i < HISI_MAX_SENSORS; i++)
+ hisi_thermal_toggle_sensor(&data->sensors[i], true);
+
+ return 0;
+
+err_get_sensor_data:
+ clk_disable_unprepare(data->clk);
+
+ return ret;
+}
+
+static int hisi_thermal_remove(struct platform_device *pdev)
+{
+ struct hisi_thermal_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < HISI_MAX_SENSORS; i++) {
+ struct hisi_thermal_sensor *sensor = &data->sensors[i];
+
+ hisi_thermal_toggle_sensor(sensor, false);
+ thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
+ }
+
+ hisi_thermal_disable_sensor(data);
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hisi_thermal_suspend(struct device *dev)
+{
+ struct hisi_thermal_data *data = dev_get_drvdata(dev);
+
+ hisi_thermal_disable_sensor(data);
+ data->irq_enabled = false;
+
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int hisi_thermal_resume(struct device *dev)
+{
+ struct hisi_thermal_data *data = dev_get_drvdata(dev);
+
+ clk_prepare_enable(data->clk);
+
+ data->irq_enabled = true;
+ hisi_thermal_enable_bind_irq_sensor(data);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(hisi_thermal_pm_ops,
+ hisi_thermal_suspend, hisi_thermal_resume);
+
+static struct platform_driver hisi_thermal_driver = {
+ .driver = {
+ .name = "hisi_thermal",
+ .owner = THIS_MODULE,
+ .pm = &hisi_thermal_pm_ops,
+ .of_match_table = of_hisi_thermal_match,
+ },
+ .probe = hisi_thermal_probe,
+ .remove = hisi_thermal_remove,
+};
+
+module_platform_driver(hisi_thermal_driver);
+
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
+MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
+MODULE_DESCRIPTION("Hisilicon thermal driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 2ccbc07..fde4c28 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -306,7 +306,8 @@ static int imx_bind(struct thermal_zone_device *tz,
ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
THERMAL_NO_LIMIT,
- THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
if (ret) {
dev_err(&tz->device,
"binding zone %s with cdev %s failed:%d\n",
diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c
index 5e8d8e9..3df3dc3 100644
--- a/drivers/thermal/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c
@@ -16,15 +16,20 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#include "int340x_thermal_zone.h"
+#include "../intel_soc_dts_iosf.h"
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
+/* Skylake thermal reporting device */
+#define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903
+
/* Braswell thermal reporting device */
#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
@@ -42,6 +47,7 @@ struct proc_thermal_device {
struct acpi_device *adev;
struct power_config power_limits[2];
struct int34x_thermal_zone *int340x_zone;
+ struct intel_soc_dts_sensors *soc_dts;
};
enum proc_thermal_emum_mode_type {
@@ -308,6 +314,18 @@ static int int3401_remove(struct platform_device *pdev)
return 0;
}
+static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid)
+{
+ struct proc_thermal_device *proc_priv;
+ struct pci_dev *pdev = devid;
+
+ proc_priv = pci_get_drvdata(pdev);
+
+ intel_soc_dts_iosf_interrupt_handler(proc_priv->soc_dts);
+
+ return IRQ_HANDLED;
+}
+
static int proc_thermal_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *unused)
{
@@ -334,18 +352,57 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, proc_priv);
proc_thermal_emum_mode = PROC_THERMAL_PCI;
+ if (pdev->device == PCI_DEVICE_ID_PROC_BSW_THERMAL) {
+ /*
+ * Enumerate additional DTS sensors available via IOSF.
+ * But we are not treating as a failure condition, if
+ * there are no aux DTSs enabled or fails. This driver
+ * already exposes sensors, which can be accessed via
+ * ACPI/MSR. So we don't want to fail for auxiliary DTSs.
+ */
+ proc_priv->soc_dts = intel_soc_dts_iosf_init(
+ INTEL_SOC_DTS_INTERRUPT_MSI, 2, 0);
+
+ if (proc_priv->soc_dts && pdev->irq) {
+ ret = pci_enable_msi(pdev);
+ if (!ret) {
+ ret = request_threaded_irq(pdev->irq, NULL,
+ proc_thermal_pci_msi_irq,
+ IRQF_ONESHOT, "proc_thermal",
+ pdev);
+ if (ret) {
+ intel_soc_dts_iosf_exit(
+ proc_priv->soc_dts);
+ pci_disable_msi(pdev);
+ proc_priv->soc_dts = NULL;
+ }
+ }
+ } else
+ dev_err(&pdev->dev, "No auxiliary DTSs enabled\n");
+ }
+
return 0;
}
static void proc_thermal_pci_remove(struct pci_dev *pdev)
{
- proc_thermal_remove(pci_get_drvdata(pdev));
+ struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
+
+ if (proc_priv->soc_dts) {
+ intel_soc_dts_iosf_exit(proc_priv->soc_dts);
+ if (pdev->irq) {
+ free_irq(pdev->irq, pdev);
+ pci_disable_msi(pdev);
+ }
+ }
+ proc_thermal_remove(proc_priv);
pci_disable_device(pdev);
}
static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
{ 0, },
};
diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c
index 725718e..2e67161 100644
--- a/drivers/thermal/intel_powerclamp.c
+++ b/drivers/thermal/intel_powerclamp.c
@@ -697,6 +697,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = {
{ X86_VENDOR_INTEL, 6, 0x4d},
{ X86_VENDOR_INTEL, 6, 0x4f},
{ X86_VENDOR_INTEL, 6, 0x56},
+ { X86_VENDOR_INTEL, 6, 0x57},
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
diff --git a/drivers/thermal/intel_quark_dts_thermal.c b/drivers/thermal/intel_quark_dts_thermal.c
new file mode 100644
index 0000000..4434ec8
--- /dev/null
+++ b/drivers/thermal/intel_quark_dts_thermal.c
@@ -0,0 +1,473 @@
+/*
+ * intel_quark_dts_thermal.c
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Contact Information:
+ * Ong Boon Leong <boon.leong.ong@intel.com>
+ * Intel Malaysia, Penang
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Quark DTS thermal driver is implemented by referencing
+ * intel_soc_dts_thermal.c.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/thermal.h>
+#include <asm/cpu_device_id.h>
+#include <asm/iosf_mbi.h>
+
+#define X86_FAMILY_QUARK 0x5
+#define X86_MODEL_QUARK_X1000 0x9
+
+/* DTS reset is programmed via QRK_MBI_UNIT_SOC */
+#define QRK_DTS_REG_OFFSET_RESET 0x34
+#define QRK_DTS_RESET_BIT BIT(0)
+
+/* DTS enable is programmed via QRK_MBI_UNIT_RMU */
+#define QRK_DTS_REG_OFFSET_ENABLE 0xB0
+#define QRK_DTS_ENABLE_BIT BIT(15)
+
+/* Temperature Register is read via QRK_MBI_UNIT_RMU */
+#define QRK_DTS_REG_OFFSET_TEMP 0xB1
+#define QRK_DTS_MASK_TEMP 0xFF
+#define QRK_DTS_OFFSET_TEMP 0
+#define QRK_DTS_OFFSET_REL_TEMP 16
+#define QRK_DTS_TEMP_BASE 50
+
+/* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */
+#define QRK_DTS_REG_OFFSET_PTPS 0xB2
+#define QRK_DTS_MASK_TP_THRES 0xFF
+#define QRK_DTS_SHIFT_TP 8
+#define QRK_DTS_ID_TP_CRITICAL 0
+#define QRK_DTS_SAFE_TP_THRES 105
+
+/* Thermal Sensor Register Lock */
+#define QRK_DTS_REG_OFFSET_LOCK 0x71
+#define QRK_DTS_LOCK_BIT BIT(5)
+
+/* Quark DTS has 2 trip points: hot & catastrophic */
+#define QRK_MAX_DTS_TRIPS 2
+/* If DTS not locked, all trip points are configurable */
+#define QRK_DTS_WR_MASK_SET 0x3
+/* If DTS locked, all trip points are not configurable */
+#define QRK_DTS_WR_MASK_CLR 0
+
+#define DEFAULT_POLL_DELAY 2000
+
+struct soc_sensor_entry {
+ bool locked;
+ u32 store_ptps;
+ u32 store_dts_enable;
+ enum thermal_device_mode mode;
+ struct thermal_zone_device *tzone;
+};
+
+static struct soc_sensor_entry *soc_dts;
+
+static int polling_delay = DEFAULT_POLL_DELAY;
+module_param(polling_delay, int, 0644);
+MODULE_PARM_DESC(polling_delay,
+ "Polling interval for checking trip points (in milliseconds)");
+
+static DEFINE_MUTEX(dts_update_mutex);
+
+static int soc_dts_enable(struct thermal_zone_device *tzd)
+{
+ u32 out;
+ struct soc_sensor_entry *aux_entry = tzd->devdata;
+ int ret;
+
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_ENABLE, &out);
+ if (ret)
+ return ret;
+
+ if (out & QRK_DTS_ENABLE_BIT) {
+ aux_entry->mode = THERMAL_DEVICE_ENABLED;
+ return 0;
+ }
+
+ if (!aux_entry->locked) {
+ out |= QRK_DTS_ENABLE_BIT;
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE, out);
+ if (ret)
+ return ret;
+
+ aux_entry->mode = THERMAL_DEVICE_ENABLED;
+ } else {
+ aux_entry->mode = THERMAL_DEVICE_DISABLED;
+ pr_info("DTS is locked. Cannot enable DTS\n");
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+static int soc_dts_disable(struct thermal_zone_device *tzd)
+{
+ u32 out;
+ struct soc_sensor_entry *aux_entry = tzd->devdata;
+ int ret;
+
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_ENABLE, &out);
+ if (ret)
+ return ret;
+
+ if (!(out & QRK_DTS_ENABLE_BIT)) {
+ aux_entry->mode = THERMAL_DEVICE_DISABLED;
+ return 0;
+ }
+
+ if (!aux_entry->locked) {
+ out &= ~QRK_DTS_ENABLE_BIT;
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE, out);
+
+ if (ret)
+ return ret;
+
+ aux_entry->mode = THERMAL_DEVICE_DISABLED;
+ } else {
+ aux_entry->mode = THERMAL_DEVICE_ENABLED;
+ pr_info("DTS is locked. Cannot disable DTS\n");
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+static int _get_trip_temp(int trip, unsigned long *temp)
+{
+ int status;
+ u32 out;
+
+ mutex_lock(&dts_update_mutex);
+ status = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_PTPS, &out);
+ mutex_unlock(&dts_update_mutex);
+
+ if (status)
+ return status;
+
+ /*
+ * Thermal Sensor Programmable Trip Point Register has 8-bit
+ * fields for critical (catastrophic) and hot set trip point
+ * thresholds. The threshold value is always offset by its
+ * temperature base (50 degree Celsius).
+ */
+ *temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
+ *temp -= QRK_DTS_TEMP_BASE;
+
+ return 0;
+}
+
+static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
+ int trip, unsigned long *temp)
+{
+ return _get_trip_temp(trip, temp);
+}
+
+static inline int sys_get_crit_temp(struct thermal_zone_device *tzd,
+ unsigned long *temp)
+{
+ return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
+}
+
+static int update_trip_temp(struct soc_sensor_entry *aux_entry,
+ int trip, unsigned long temp)
+{
+ u32 out;
+ u32 temp_out;
+ u32 store_ptps;
+ int ret;
+
+ mutex_lock(&dts_update_mutex);
+ if (aux_entry->locked) {
+ ret = -EPERM;
+ goto failed;
+ }
+
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_PTPS, &store_ptps);
+ if (ret)
+ goto failed;
+
+ /*
+ * Protection against unsafe trip point thresdhold value.
+ * As Quark X1000 data-sheet does not provide any recommendation
+ * regarding the safe trip point threshold value to use, we choose
+ * the safe value according to the threshold value set by UEFI BIOS.
+ */
+ if (temp > QRK_DTS_SAFE_TP_THRES)
+ temp = QRK_DTS_SAFE_TP_THRES;
+
+ /*
+ * Thermal Sensor Programmable Trip Point Register has 8-bit
+ * fields for critical (catastrophic) and hot set trip point
+ * thresholds. The threshold value is always offset by its
+ * temperature base (50 degree Celsius).
+ */
+ temp_out = temp + QRK_DTS_TEMP_BASE;
+ out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES <<
+ (trip * QRK_DTS_SHIFT_TP)));
+ out |= (temp_out & QRK_DTS_MASK_TP_THRES) <<
+ (trip * QRK_DTS_SHIFT_TP);
+
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
+ QRK_DTS_REG_OFFSET_PTPS, out);
+
+failed:
+ mutex_unlock(&dts_update_mutex);
+ return ret;
+}
+
+static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
+ unsigned long temp)
+{
+ return update_trip_temp(tzd->devdata, trip, temp);
+}
+
+static int sys_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ if (trip)
+ *type = THERMAL_TRIP_HOT;
+ else
+ *type = THERMAL_TRIP_CRITICAL;
+
+ return 0;
+}
+
+static int sys_get_curr_temp(struct thermal_zone_device *tzd,
+ unsigned long *temp)
+{
+ u32 out;
+ int ret;
+
+ mutex_lock(&dts_update_mutex);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_TEMP, &out);
+ mutex_unlock(&dts_update_mutex);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Thermal Sensor Temperature Register has 8-bit field
+ * for temperature value (offset by temperature base
+ * 50 degree Celsius).
+ */
+ out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP;
+ *temp = out - QRK_DTS_TEMP_BASE;
+
+ return 0;
+}
+
+static int sys_get_mode(struct thermal_zone_device *tzd,
+ enum thermal_device_mode *mode)
+{
+ struct soc_sensor_entry *aux_entry = tzd->devdata;
+ *mode = aux_entry->mode;
+ return 0;
+}
+
+static int sys_set_mode(struct thermal_zone_device *tzd,
+ enum thermal_device_mode mode)
+{
+ int ret;
+
+ mutex_lock(&dts_update_mutex);
+ if (mode == THERMAL_DEVICE_ENABLED)
+ ret = soc_dts_enable(tzd);
+ else
+ ret = soc_dts_disable(tzd);
+ mutex_unlock(&dts_update_mutex);
+
+ return ret;
+}
+
+static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = sys_get_curr_temp,
+ .get_trip_temp = sys_get_trip_temp,
+ .get_trip_type = sys_get_trip_type,
+ .set_trip_temp = sys_set_trip_temp,
+ .get_crit_temp = sys_get_crit_temp,
+ .get_mode = sys_get_mode,
+ .set_mode = sys_set_mode,
+};
+
+static void free_soc_dts(struct soc_sensor_entry *aux_entry)
+{
+ if (aux_entry) {
+ if (!aux_entry->locked) {
+ mutex_lock(&dts_update_mutex);
+ iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE,
+ aux_entry->store_dts_enable);
+
+ iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
+ QRK_DTS_REG_OFFSET_PTPS,
+ aux_entry->store_ptps);
+ mutex_unlock(&dts_update_mutex);
+ }
+ thermal_zone_device_unregister(aux_entry->tzone);
+ kfree(aux_entry);
+ }
+}
+
+static struct soc_sensor_entry *alloc_soc_dts(void)
+{
+ struct soc_sensor_entry *aux_entry;
+ int err;
+ u32 out;
+ int wr_mask;
+
+ aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
+ if (!aux_entry) {
+ err = -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Check if DTS register is locked */
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_LOCK,
+ &out);
+ if (err)
+ goto err_ret;
+
+ if (out & QRK_DTS_LOCK_BIT) {
+ aux_entry->locked = true;
+ wr_mask = QRK_DTS_WR_MASK_CLR;
+ } else {
+ aux_entry->locked = false;
+ wr_mask = QRK_DTS_WR_MASK_SET;
+ }
+
+ /* Store DTS default state if DTS registers are not locked */
+ if (!aux_entry->locked) {
+ /* Store DTS default enable for restore on exit */
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_ENABLE,
+ &aux_entry->store_dts_enable);
+ if (err)
+ goto err_ret;
+
+ /* Store DTS default PTPS register for restore on exit */
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
+ QRK_DTS_REG_OFFSET_PTPS,
+ &aux_entry->store_ptps);
+ if (err)
+ goto err_ret;
+ }
+
+ aux_entry->tzone = thermal_zone_device_register("quark_dts",
+ QRK_MAX_DTS_TRIPS,
+ wr_mask,
+ aux_entry, &tzone_ops, NULL, 0, polling_delay);
+ if (IS_ERR(aux_entry->tzone)) {
+ err = PTR_ERR(aux_entry->tzone);
+ goto err_ret;
+ }
+
+ mutex_lock(&dts_update_mutex);
+ err = soc_dts_enable(aux_entry->tzone);
+ mutex_unlock(&dts_update_mutex);
+ if (err)
+ goto err_aux_status;
+
+ return aux_entry;
+
+err_aux_status:
+ thermal_zone_device_unregister(aux_entry->tzone);
+err_ret:
+ kfree(aux_entry);
+ return ERR_PTR(err);
+}
+
+static const struct x86_cpu_id qrk_thermal_ids[] __initconst = {
+ { X86_VENDOR_INTEL, X86_FAMILY_QUARK, X86_MODEL_QUARK_X1000 },
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids);
+
+static int __init intel_quark_thermal_init(void)
+{
+ int err = 0;
+
+ if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available())
+ return -ENODEV;
+
+ soc_dts = alloc_soc_dts();
+ if (IS_ERR(soc_dts)) {
+ err = PTR_ERR(soc_dts);
+ goto err_free;
+ }
+
+ return 0;
+
+err_free:
+ free_soc_dts(soc_dts);
+ return err;
+}
+
+static void __exit intel_quark_thermal_exit(void)
+{
+ free_soc_dts(soc_dts);
+}
+
+module_init(intel_quark_thermal_init)
+module_exit(intel_quark_thermal_exit)
+
+MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver");
+MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c
new file mode 100644
index 0000000..42e4b6a
--- /dev/null
+++ b/drivers/thermal/intel_soc_dts_iosf.c
@@ -0,0 +1,478 @@
+/*
+ * intel_soc_dts_iosf.c
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/iosf_mbi.h>
+#include "intel_soc_dts_iosf.h"
+
+#define SOC_DTS_OFFSET_ENABLE 0xB0
+#define SOC_DTS_OFFSET_TEMP 0xB1
+
+#define SOC_DTS_OFFSET_PTPS 0xB2
+#define SOC_DTS_OFFSET_PTTS 0xB3
+#define SOC_DTS_OFFSET_PTTSS 0xB4
+#define SOC_DTS_OFFSET_PTMC 0x80
+#define SOC_DTS_TE_AUX0 0xB5
+#define SOC_DTS_TE_AUX1 0xB6
+
+#define SOC_DTS_AUX0_ENABLE_BIT BIT(0)
+#define SOC_DTS_AUX1_ENABLE_BIT BIT(1)
+#define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16)
+#define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17)
+#define SOC_DTS_TE_SCI_ENABLE BIT(9)
+#define SOC_DTS_TE_SMI_ENABLE BIT(10)
+#define SOC_DTS_TE_MSI_ENABLE BIT(11)
+#define SOC_DTS_TE_APICA_ENABLE BIT(14)
+#define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4)
+
+/* DTS encoding for TJ MAX temperature */
+#define SOC_DTS_TJMAX_ENCODING 0x7F
+
+/* Only 2 out of 4 is allowed for OSPM */
+#define SOC_MAX_DTS_TRIPS 2
+
+/* Mask for two trips in status bits */
+#define SOC_DTS_TRIP_MASK 0x03
+
+/* DTS0 and DTS 1 */
+#define SOC_MAX_DTS_SENSORS 2
+
+static int get_tj_max(u32 *tj_max)
+{
+ u32 eax, edx;
+ u32 val;
+ int err;
+
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err)
+ goto err_ret;
+ else {
+ val = (eax >> 16) & 0xff;
+ if (val)
+ *tj_max = val * 1000;
+ else {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ }
+
+ return 0;
+err_ret:
+ *tj_max = 0;
+
+ return err;
+}
+
+static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
+ unsigned long *temp)
+{
+ int status;
+ u32 out;
+ struct intel_soc_dts_sensor_entry *dts;
+ struct intel_soc_dts_sensors *sensors;
+
+ dts = tzd->devdata;
+ sensors = dts->sensors;
+ mutex_lock(&sensors->dts_update_lock);
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTPS, &out);
+ mutex_unlock(&sensors->dts_update_lock);
+ if (status)
+ return status;
+
+ out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING;
+ if (!out)
+ *temp = 0;
+ else
+ *temp = sensors->tj_max - out * 1000;
+
+ return 0;
+}
+
+static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
+ int thres_index, unsigned long temp,
+ enum thermal_trip_type trip_type)
+{
+ int status;
+ u32 temp_out;
+ u32 out;
+ u32 store_ptps;
+ u32 store_ptmc;
+ u32 store_te_out;
+ u32 te_out;
+ u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE;
+ struct intel_soc_dts_sensors *sensors = dts->sensors;
+
+ if (sensors->intr_type == INTEL_SOC_DTS_INTERRUPT_MSI)
+ int_enable_bit |= SOC_DTS_TE_MSI_ENABLE;
+
+ temp_out = (sensors->tj_max - temp) / 1000;
+
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTPS, &store_ptps);
+ if (status)
+ return status;
+
+ out = (store_ptps & ~(0xFF << (thres_index * 8)));
+ out |= (temp_out & 0xFF) << (thres_index * 8);
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTPS, out);
+ if (status)
+ return status;
+
+ pr_debug("update_trip_temp PTPS = %x\n", out);
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTMC, &out);
+ if (status)
+ goto err_restore_ptps;
+
+ store_ptmc = out;
+
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_TE_AUX0 + thres_index,
+ &te_out);
+ if (status)
+ goto err_restore_ptmc;
+
+ store_te_out = te_out;
+ /* Enable for CPU module 0 and module 1 */
+ out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT |
+ SOC_DTS_CPU_MODULE1_ENABLE_BIT);
+ if (temp) {
+ if (thres_index)
+ out |= SOC_DTS_AUX1_ENABLE_BIT;
+ else
+ out |= SOC_DTS_AUX0_ENABLE_BIT;
+ te_out |= int_enable_bit;
+ } else {
+ if (thres_index)
+ out &= ~SOC_DTS_AUX1_ENABLE_BIT;
+ else
+ out &= ~SOC_DTS_AUX0_ENABLE_BIT;
+ te_out &= ~int_enable_bit;
+ }
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTMC, out);
+ if (status)
+ goto err_restore_te_out;
+
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_TE_AUX0 + thres_index,
+ te_out);
+ if (status)
+ goto err_restore_te_out;
+
+ dts->trip_types[thres_index] = trip_type;
+
+ return 0;
+err_restore_te_out:
+ iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTMC, store_te_out);
+err_restore_ptmc:
+ iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTMC, store_ptmc);
+err_restore_ptps:
+ iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTPS, store_ptps);
+ /* Nothing we can do if restore fails */
+
+ return status;
+}
+
+static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
+ unsigned long temp)
+{
+ struct intel_soc_dts_sensor_entry *dts = tzd->devdata;
+ struct intel_soc_dts_sensors *sensors = dts->sensors;
+ int status;
+
+ if (temp > sensors->tj_max)
+ return -EINVAL;
+
+ mutex_lock(&sensors->dts_update_lock);
+ status = update_trip_temp(tzd->devdata, trip, temp,
+ dts->trip_types[trip]);
+ mutex_unlock(&sensors->dts_update_lock);
+
+ return status;
+}
+
+static int sys_get_trip_type(struct thermal_zone_device *tzd,
+ int trip, enum thermal_trip_type *type)
+{
+ struct intel_soc_dts_sensor_entry *dts;
+
+ dts = tzd->devdata;
+
+ *type = dts->trip_types[trip];
+
+ return 0;
+}
+
+static int sys_get_curr_temp(struct thermal_zone_device *tzd,
+ unsigned long *temp)
+{
+ int status;
+ u32 out;
+ struct intel_soc_dts_sensor_entry *dts;
+ struct intel_soc_dts_sensors *sensors;
+
+ dts = tzd->devdata;
+ sensors = dts->sensors;
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_TEMP, &out);
+ if (status)
+ return status;
+
+ out = (out & dts->temp_mask) >> dts->temp_shift;
+ out -= SOC_DTS_TJMAX_ENCODING;
+ *temp = sensors->tj_max - out * 1000;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = sys_get_curr_temp,
+ .get_trip_temp = sys_get_trip_temp,
+ .get_trip_type = sys_get_trip_type,
+ .set_trip_temp = sys_set_trip_temp,
+};
+
+static int soc_dts_enable(int id)
+{
+ u32 out;
+ int ret;
+
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_ENABLE, &out);
+ if (ret)
+ return ret;
+
+ if (!(out & BIT(id))) {
+ out |= BIT(id);
+ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_ENABLE, out);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static void remove_dts_thermal_zone(struct intel_soc_dts_sensor_entry *dts)
+{
+ if (dts) {
+ iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_ENABLE, dts->store_status);
+ thermal_zone_device_unregister(dts->tzone);
+ }
+}
+
+static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
+ bool notification_support, int trip_cnt,
+ int read_only_trip_cnt)
+{
+ char name[10];
+ int trip_count = 0;
+ int trip_mask = 0;
+ u32 store_ptps;
+ int ret;
+ int i;
+
+ /* Store status to restor on exit */
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_ENABLE,
+ &dts->store_status);
+ if (ret)
+ goto err_ret;
+
+ dts->id = id;
+ dts->temp_mask = 0x00FF << (id * 8);
+ dts->temp_shift = id * 8;
+ if (notification_support) {
+ trip_count = min(SOC_MAX_DTS_TRIPS, trip_cnt);
+ trip_mask = BIT(trip_count - read_only_trip_cnt) - 1;
+ }
+
+ /* Check if the writable trip we provide is not used by BIOS */
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTPS, &store_ptps);
+ if (ret)
+ trip_mask = 0;
+ else {
+ for (i = 0; i < trip_count; ++i) {
+ if (trip_mask & BIT(i))
+ if (store_ptps & (0xff << (i * 8)))
+ trip_mask &= ~BIT(i);
+ }
+ }
+ dts->trip_mask = trip_mask;
+ dts->trip_count = trip_count;
+ snprintf(name, sizeof(name), "soc_dts%d", id);
+ dts->tzone = thermal_zone_device_register(name,
+ trip_count,
+ trip_mask,
+ dts, &tzone_ops,
+ NULL, 0, 0);
+ if (IS_ERR(dts->tzone)) {
+ ret = PTR_ERR(dts->tzone);
+ goto err_ret;
+ }
+
+ ret = soc_dts_enable(id);
+ if (ret)
+ goto err_enable;
+
+ return 0;
+err_enable:
+ thermal_zone_device_unregister(dts->tzone);
+err_ret:
+ return ret;
+}
+
+int intel_soc_dts_iosf_add_read_only_critical_trip(
+ struct intel_soc_dts_sensors *sensors, int critical_offset)
+{
+ int i, j;
+
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
+ for (j = 0; j < sensors->soc_dts[i].trip_count; ++j) {
+ if (!(sensors->soc_dts[i].trip_mask & BIT(j))) {
+ return update_trip_temp(&sensors->soc_dts[i], j,
+ sensors->tj_max - critical_offset,
+ THERMAL_TRIP_CRITICAL);
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_add_read_only_critical_trip);
+
+void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors)
+{
+ u32 sticky_out;
+ int status;
+ u32 ptmc_out;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sensors->intr_notify_lock, flags);
+
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTMC, &ptmc_out);
+ ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT;
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTMC, ptmc_out);
+
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ SOC_DTS_OFFSET_PTTSS, &sticky_out);
+ pr_debug("status %d PTTSS %x\n", status, sticky_out);
+ if (sticky_out & SOC_DTS_TRIP_MASK) {
+ int i;
+ /* reset sticky bit */
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ SOC_DTS_OFFSET_PTTSS, sticky_out);
+ spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
+
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
+ pr_debug("TZD update for zone %d\n", i);
+ thermal_zone_device_update(sensors->soc_dts[i].tzone);
+ }
+ } else
+ spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
+}
+EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_interrupt_handler);
+
+struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
+ enum intel_soc_dts_interrupt_type intr_type, int trip_count,
+ int read_only_trip_count)
+{
+ struct intel_soc_dts_sensors *sensors;
+ bool notification;
+ u32 tj_max;
+ int ret;
+ int i;
+
+ if (!iosf_mbi_available())
+ return ERR_PTR(-ENODEV);
+
+ if (!trip_count || read_only_trip_count > trip_count)
+ return ERR_PTR(-EINVAL);
+
+ if (get_tj_max(&tj_max))
+ return ERR_PTR(-EINVAL);
+
+ sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
+ if (!sensors)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&sensors->intr_notify_lock);
+ mutex_init(&sensors->dts_update_lock);
+ sensors->intr_type = intr_type;
+ sensors->tj_max = tj_max;
+ if (intr_type == INTEL_SOC_DTS_INTERRUPT_NONE)
+ notification = false;
+ else
+ notification = true;
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
+ sensors->soc_dts[i].sensors = sensors;
+ ret = add_dts_thermal_zone(i, &sensors->soc_dts[i],
+ notification, trip_count,
+ read_only_trip_count);
+ if (ret)
+ goto err_free;
+ }
+
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
+ ret = update_trip_temp(&sensors->soc_dts[i], 0, 0,
+ THERMAL_TRIP_PASSIVE);
+ if (ret)
+ goto err_remove_zone;
+
+ ret = update_trip_temp(&sensors->soc_dts[i], 1, 0,
+ THERMAL_TRIP_PASSIVE);
+ if (ret)
+ goto err_remove_zone;
+ }
+
+ return sensors;
+err_remove_zone:
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
+ remove_dts_thermal_zone(&sensors->soc_dts[i]);
+
+err_free:
+ kfree(sensors);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_init);
+
+void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors)
+{
+ int i;
+
+ for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
+ update_trip_temp(&sensors->soc_dts[i], 0, 0, 0);
+ update_trip_temp(&sensors->soc_dts[i], 1, 0, 0);
+ remove_dts_thermal_zone(&sensors->soc_dts[i]);
+ }
+ kfree(sensors);
+}
+EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel_soc_dts_iosf.h b/drivers/thermal/intel_soc_dts_iosf.h
new file mode 100644
index 0000000..625e37b
--- /dev/null
+++ b/drivers/thermal/intel_soc_dts_iosf.h
@@ -0,0 +1,62 @@
+/*
+ * intel_soc_dts_iosf.h
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 _INTEL_SOC_DTS_IOSF_CORE_H
+#define _INTEL_SOC_DTS_IOSF_CORE_H
+
+#include <linux/thermal.h>
+
+/* DTS0 and DTS 1 */
+#define SOC_MAX_DTS_SENSORS 2
+
+enum intel_soc_dts_interrupt_type {
+ INTEL_SOC_DTS_INTERRUPT_NONE,
+ INTEL_SOC_DTS_INTERRUPT_APIC,
+ INTEL_SOC_DTS_INTERRUPT_MSI,
+ INTEL_SOC_DTS_INTERRUPT_SCI,
+ INTEL_SOC_DTS_INTERRUPT_SMI,
+};
+
+struct intel_soc_dts_sensors;
+
+struct intel_soc_dts_sensor_entry {
+ int id;
+ u32 temp_mask;
+ u32 temp_shift;
+ u32 store_status;
+ u32 trip_mask;
+ u32 trip_count;
+ enum thermal_trip_type trip_types[2];
+ struct thermal_zone_device *tzone;
+ struct intel_soc_dts_sensors *sensors;
+};
+
+struct intel_soc_dts_sensors {
+ u32 tj_max;
+ spinlock_t intr_notify_lock;
+ struct mutex dts_update_lock;
+ enum intel_soc_dts_interrupt_type intr_type;
+ struct intel_soc_dts_sensor_entry soc_dts[SOC_MAX_DTS_SENSORS];
+};
+
+struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
+ enum intel_soc_dts_interrupt_type intr_type, int trip_count,
+ int read_only_trip_count);
+void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors);
+void intel_soc_dts_iosf_interrupt_handler(
+ struct intel_soc_dts_sensors *sensors);
+int intel_soc_dts_iosf_add_read_only_critical_trip(
+ struct intel_soc_dts_sensors *sensors, int critical_offset);
+#endif
diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel_soc_dts_thermal.c
index 9013505..4ebb31a3 100644
--- a/drivers/thermal/intel_soc_dts_thermal.c
+++ b/drivers/thermal/intel_soc_dts_thermal.c
@@ -16,431 +16,54 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
-#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/thermal.h>
#include <asm/cpu_device_id.h>
-#include <asm/iosf_mbi.h>
-
-#define SOC_DTS_OFFSET_ENABLE 0xB0
-#define SOC_DTS_OFFSET_TEMP 0xB1
-
-#define SOC_DTS_OFFSET_PTPS 0xB2
-#define SOC_DTS_OFFSET_PTTS 0xB3
-#define SOC_DTS_OFFSET_PTTSS 0xB4
-#define SOC_DTS_OFFSET_PTMC 0x80
-#define SOC_DTS_TE_AUX0 0xB5
-#define SOC_DTS_TE_AUX1 0xB6
-
-#define SOC_DTS_AUX0_ENABLE_BIT BIT(0)
-#define SOC_DTS_AUX1_ENABLE_BIT BIT(1)
-#define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16)
-#define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17)
-#define SOC_DTS_TE_SCI_ENABLE BIT(9)
-#define SOC_DTS_TE_SMI_ENABLE BIT(10)
-#define SOC_DTS_TE_MSI_ENABLE BIT(11)
-#define SOC_DTS_TE_APICA_ENABLE BIT(14)
-#define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4)
-
-/* DTS encoding for TJ MAX temperature */
-#define SOC_DTS_TJMAX_ENCODING 0x7F
-
-/* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */
-#define BYT_SOC_DTS_APIC_IRQ 86
-
-/* Only 2 out of 4 is allowed for OSPM */
-#define SOC_MAX_DTS_TRIPS 2
-
-/* Mask for two trips in status bits */
-#define SOC_DTS_TRIP_MASK 0x03
-
-/* DTS0 and DTS 1 */
-#define SOC_MAX_DTS_SENSORS 2
+#include "intel_soc_dts_iosf.h"
#define CRITICAL_OFFSET_FROM_TJ_MAX 5000
-struct soc_sensor_entry {
- int id;
- u32 tj_max;
- u32 temp_mask;
- u32 temp_shift;
- u32 store_status;
- struct thermal_zone_device *tzone;
-};
-
-static struct soc_sensor_entry *soc_dts[SOC_MAX_DTS_SENSORS];
-
static int crit_offset = CRITICAL_OFFSET_FROM_TJ_MAX;
module_param(crit_offset, int, 0644);
MODULE_PARM_DESC(crit_offset,
"Critical Temperature offset from tj max in millidegree Celsius.");
-static DEFINE_MUTEX(aux_update_mutex);
-static spinlock_t intr_notify_lock;
-static int soc_dts_thres_irq;
-
-static int get_tj_max(u32 *tj_max)
-{
- u32 eax, edx;
- u32 val;
- int err;
-
- err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (err)
- goto err_ret;
- else {
- val = (eax >> 16) & 0xff;
- if (val)
- *tj_max = val * 1000;
- else {
- err = -EINVAL;
- goto err_ret;
- }
- }
-
- return 0;
-err_ret:
- *tj_max = 0;
-
- return err;
-}
-
-static int sys_get_trip_temp(struct thermal_zone_device *tzd,
- int trip, unsigned long *temp)
-{
- int status;
- u32 out;
- struct soc_sensor_entry *aux_entry;
-
- aux_entry = tzd->devdata;
-
- if (!trip) {
- /* Just return the critical temp */
- *temp = aux_entry->tj_max - crit_offset;
- return 0;
- }
-
- mutex_lock(&aux_update_mutex);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_PTPS, &out);
- mutex_unlock(&aux_update_mutex);
- if (status)
- return status;
-
- out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING;
-
- if (!out)
- *temp = 0;
- else
- *temp = aux_entry->tj_max - out * 1000;
-
- return 0;
-}
-
-static int update_trip_temp(struct soc_sensor_entry *aux_entry,
- int thres_index, unsigned long temp)
-{
- int status;
- u32 temp_out;
- u32 out;
- u32 store_ptps;
- u32 store_ptmc;
- u32 store_te_out;
- u32 te_out;
-
- u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE |
- SOC_DTS_TE_MSI_ENABLE;
-
- temp_out = (aux_entry->tj_max - temp) / 1000;
-
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_PTPS, &store_ptps);
- if (status)
- return status;
-
- out = (store_ptps & ~(0xFF << (thres_index * 8)));
- out |= (temp_out & 0xFF) << (thres_index * 8);
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTPS, out);
- if (status)
- return status;
- pr_debug("update_trip_temp PTPS = %x\n", out);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_PTMC, &out);
- if (status)
- goto err_restore_ptps;
-
- store_ptmc = out;
-
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_TE_AUX0 + thres_index,
- &te_out);
- if (status)
- goto err_restore_ptmc;
-
- store_te_out = te_out;
-
- /* Enable for CPU module 0 and module 1 */
- out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT |
- SOC_DTS_CPU_MODULE1_ENABLE_BIT);
- if (temp) {
- if (thres_index)
- out |= SOC_DTS_AUX1_ENABLE_BIT;
- else
- out |= SOC_DTS_AUX0_ENABLE_BIT;
- te_out |= int_enable_bit;
- } else {
- if (thres_index)
- out &= ~SOC_DTS_AUX1_ENABLE_BIT;
- else
- out &= ~SOC_DTS_AUX0_ENABLE_BIT;
- te_out &= ~int_enable_bit;
- }
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTMC, out);
- if (status)
- goto err_restore_te_out;
-
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_TE_AUX0 + thres_index,
- te_out);
- if (status)
- goto err_restore_te_out;
-
- return 0;
-
-err_restore_te_out:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTMC, store_te_out);
-err_restore_ptmc:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTMC, store_ptmc);
-err_restore_ptps:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTPS, store_ptps);
- /* Nothing we can do if restore fails */
-
- return status;
-}
-
-static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
- unsigned long temp)
-{
- struct soc_sensor_entry *aux_entry = tzd->devdata;
- int status;
-
- if (temp > (aux_entry->tj_max - crit_offset))
- return -EINVAL;
-
- mutex_lock(&aux_update_mutex);
- status = update_trip_temp(tzd->devdata, trip, temp);
- mutex_unlock(&aux_update_mutex);
-
- return status;
-}
-
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
-{
- if (trip)
- *type = THERMAL_TRIP_PASSIVE;
- else
- *type = THERMAL_TRIP_CRITICAL;
-
- return 0;
-}
-
-static int sys_get_curr_temp(struct thermal_zone_device *tzd,
- unsigned long *temp)
-{
- int status;
- u32 out;
- struct soc_sensor_entry *aux_entry;
-
- aux_entry = tzd->devdata;
-
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_TEMP, &out);
- if (status)
- return status;
-
- out = (out & aux_entry->temp_mask) >> aux_entry->temp_shift;
- out -= SOC_DTS_TJMAX_ENCODING;
- *temp = aux_entry->tj_max - out * 1000;
-
- return 0;
-}
-
-static struct thermal_zone_device_ops tzone_ops = {
- .get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
- .set_trip_temp = sys_set_trip_temp,
-};
-
-static void free_soc_dts(struct soc_sensor_entry *aux_entry)
-{
- if (aux_entry) {
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_ENABLE, aux_entry->store_status);
- thermal_zone_device_unregister(aux_entry->tzone);
- kfree(aux_entry);
- }
-}
-
-static int soc_dts_enable(int id)
-{
- u32 out;
- int ret;
-
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_ENABLE, &out);
- if (ret)
- return ret;
-
- if (!(out & BIT(id))) {
- out |= BIT(id);
- ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_ENABLE, out);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max,
- bool notification_support)
-{
- struct soc_sensor_entry *aux_entry;
- char name[10];
- int trip_count = 0;
- int trip_mask = 0;
- int err;
-
- aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
- if (!aux_entry) {
- err = -ENOMEM;
- return ERR_PTR(-ENOMEM);
- }
-
- /* Store status to restor on exit */
- err = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_ENABLE,
- &aux_entry->store_status);
- if (err)
- goto err_ret;
-
- aux_entry->id = id;
- aux_entry->tj_max = tj_max;
- aux_entry->temp_mask = 0x00FF << (id * 8);
- aux_entry->temp_shift = id * 8;
- if (notification_support) {
- trip_count = SOC_MAX_DTS_TRIPS;
- trip_mask = 0x02;
- }
- snprintf(name, sizeof(name), "soc_dts%d", id);
- aux_entry->tzone = thermal_zone_device_register(name,
- trip_count,
- trip_mask,
- aux_entry, &tzone_ops,
- NULL, 0, 0);
- if (IS_ERR(aux_entry->tzone)) {
- err = PTR_ERR(aux_entry->tzone);
- goto err_ret;
- }
-
- err = soc_dts_enable(id);
- if (err)
- goto err_aux_status;
-
- return aux_entry;
-
-err_aux_status:
- thermal_zone_device_unregister(aux_entry->tzone);
-err_ret:
- kfree(aux_entry);
- return ERR_PTR(err);
-}
-
-static void proc_thermal_interrupt(void)
-{
- u32 sticky_out;
- int status;
- u32 ptmc_out;
- unsigned long flags;
-
- spin_lock_irqsave(&intr_notify_lock, flags);
-
- /* Clear APIC interrupt */
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_PTMC, &ptmc_out);
-
- ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT;
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTMC, ptmc_out);
-
- /* Read status here */
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_PTTSS, &sticky_out);
- pr_debug("status %d PTTSS %x\n", status, sticky_out);
- if (sticky_out & SOC_DTS_TRIP_MASK) {
- int i;
- /* reset sticky bit */
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- SOC_DTS_OFFSET_PTTSS, sticky_out);
- spin_unlock_irqrestore(&intr_notify_lock, flags);
-
- for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
- pr_debug("TZD update for zone %d\n", i);
- thermal_zone_device_update(soc_dts[i]->tzone);
- }
- } else
- spin_unlock_irqrestore(&intr_notify_lock, flags);
+/* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */
+#define BYT_SOC_DTS_APIC_IRQ 86
-}
+static int soc_dts_thres_irq;
+static struct intel_soc_dts_sensors *soc_dts;
static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data)
{
- proc_thermal_interrupt();
pr_debug("proc_thermal_interrupt\n");
+ intel_soc_dts_iosf_interrupt_handler(soc_dts);
return IRQ_HANDLED;
}
static const struct x86_cpu_id soc_thermal_ids[] = {
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x37, 0, BYT_SOC_DTS_APIC_IRQ},
- { X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x4c, 0, 0},
{}
};
MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids);
static int __init intel_soc_thermal_init(void)
{
- u32 tj_max;
int err = 0;
- int i;
const struct x86_cpu_id *match_cpu;
match_cpu = x86_match_cpu(soc_thermal_ids);
if (!match_cpu)
return -ENODEV;
- if (get_tj_max(&tj_max))
- return -EINVAL;
-
- soc_dts_thres_irq = (int)match_cpu->driver_data;
-
- for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
- soc_dts[i] = alloc_soc_dts(i, tj_max,
- soc_dts_thres_irq ? true : false);
- if (IS_ERR(soc_dts[i])) {
- err = PTR_ERR(soc_dts[i]);
- goto err_free;
- }
+ /* Create a zone with 2 trips with marked as read only */
+ soc_dts = intel_soc_dts_iosf_init(INTEL_SOC_DTS_INTERRUPT_APIC, 2, 1);
+ if (IS_ERR(soc_dts)) {
+ err = PTR_ERR(soc_dts);
+ return err;
}
- spin_lock_init(&intr_notify_lock);
+ soc_dts_thres_irq = (int)match_cpu->driver_data;
if (soc_dts_thres_irq) {
err = request_threaded_irq(soc_dts_thres_irq, NULL,
@@ -449,42 +72,31 @@ static int __init intel_soc_thermal_init(void)
"soc_dts", soc_dts);
if (err) {
pr_err("request_threaded_irq ret %d\n", err);
- goto err_free;
+ goto error_irq;
}
}
- for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
- err = update_trip_temp(soc_dts[i], 0, tj_max - crit_offset);
- if (err)
- goto err_trip_temp;
- }
+ err = intel_soc_dts_iosf_add_read_only_critical_trip(soc_dts,
+ crit_offset);
+ if (err)
+ goto error_trips;
return 0;
-err_trip_temp:
- i = SOC_MAX_DTS_SENSORS;
+error_trips:
if (soc_dts_thres_irq)
free_irq(soc_dts_thres_irq, soc_dts);
-err_free:
- while (--i >= 0)
- free_soc_dts(soc_dts[i]);
+error_irq:
+ intel_soc_dts_iosf_exit(soc_dts);
return err;
}
static void __exit intel_soc_thermal_exit(void)
{
- int i;
-
- for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
- update_trip_temp(soc_dts[i], 0, 0);
-
if (soc_dts_thres_irq)
free_irq(soc_dts_thres_irq, soc_dts);
-
- for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
- free_soc_dts(soc_dts[i]);
-
+ intel_soc_dts_iosf_exit(soc_dts);
}
module_init(intel_soc_thermal_init)
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index 668fb1b..b295b2b 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -58,6 +58,8 @@ struct __thermal_bind_params {
* @mode: current thermal zone device mode (enabled/disabled)
* @passive_delay: polling interval while passive cooling is activated
* @polling_delay: zone polling interval
+ * @slope: slope of the temperature adjustment curve
+ * @offset: offset of the temperature adjustment curve
* @ntrips: number of trip points
* @trips: an array of trip points (0..ntrips - 1)
* @num_tbps: number of thermal bind params
@@ -70,6 +72,8 @@ struct __thermal_zone {
enum thermal_device_mode mode;
int passive_delay;
int polling_delay;
+ int slope;
+ int offset;
/* trip data */
int ntrips;
@@ -227,7 +231,8 @@ static int of_thermal_bind(struct thermal_zone_device *thermal,
ret = thermal_zone_bind_cooling_device(thermal,
tbp->trip_id, cdev,
tbp->max,
- tbp->min);
+ tbp->min,
+ tbp->usage);
if (ret)
return ret;
}
@@ -581,7 +586,7 @@ static int thermal_of_populate_bind_params(struct device_node *np,
u32 prop;
/* Default weight. Usage is optional */
- __tbp->usage = 0;
+ __tbp->usage = THERMAL_WEIGHT_DEFAULT;
ret = of_property_read_u32(np, "contribution", &prop);
if (ret == 0)
__tbp->usage = prop;
@@ -715,7 +720,7 @@ static int thermal_of_populate_trip(struct device_node *np,
* @np parameter and fills the read data into a __thermal_zone data structure
* and return this pointer.
*
- * TODO: Missing properties to parse: thermal-sensor-names and coefficients
+ * TODO: Missing properties to parse: thermal-sensor-names
*
* Return: On success returns a valid struct __thermal_zone,
* otherwise, it returns a corresponding ERR_PTR(). Caller must
@@ -727,7 +732,7 @@ thermal_of_build_thermal_zone(struct device_node *np)
struct device_node *child = NULL, *gchild;
struct __thermal_zone *tz;
int ret, i;
- u32 prop;
+ u32 prop, coef[2];
if (!np) {
pr_err("no thermal zone np\n");
@@ -752,6 +757,20 @@ thermal_of_build_thermal_zone(struct device_node *np)
}
tz->polling_delay = prop;
+ /*
+ * REVIST: for now, the thermal framework supports only
+ * one sensor per thermal zone. Thus, we are considering
+ * only the first two values as slope and offset.
+ */
+ ret = of_property_read_u32_array(np, "coefficients", coef, 2);
+ if (ret == 0) {
+ tz->slope = coef[0];
+ tz->offset = coef[1];
+ } else {
+ tz->slope = 1;
+ tz->offset = 0;
+ }
+
/* trips */
child = of_get_child_by_name(np, "trips");
@@ -865,6 +884,8 @@ int __init of_parse_thermal_zones(void)
for_each_child_of_node(np, child) {
struct thermal_zone_device *zone;
struct thermal_zone_params *tzp;
+ int i, mask = 0;
+ u32 prop;
/* Check whether child is enabled or not */
if (!of_device_is_available(child))
@@ -891,8 +912,18 @@ int __init of_parse_thermal_zones(void)
/* No hwmon because there might be hwmon drivers registering */
tzp->no_hwmon = true;
+ if (!of_property_read_u32(child, "sustainable-power", &prop))
+ tzp->sustainable_power = prop;
+
+ for (i = 0; i < tz->ntrips; i++)
+ mask |= 1 << i;
+
+ /* these two are left for temperature drivers to use */
+ tzp->slope = tz->slope;
+ tzp->offset = tz->offset;
+
zone = thermal_zone_device_register(child->name, tz->ntrips,
- 0, tz,
+ mask, tz,
ops, tzp,
tz->passive_delay,
tz->polling_delay);
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
new file mode 100644
index 0000000..4672250
--- /dev/null
+++ b/drivers/thermal/power_allocator.c
@@ -0,0 +1,539 @@
+/*
+ * A power allocator to manage temperature
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "Power allocator: " fmt
+
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/thermal_power_allocator.h>
+
+#include "thermal_core.h"
+
+#define FRAC_BITS 10
+#define int_to_frac(x) ((x) << FRAC_BITS)
+#define frac_to_int(x) ((x) >> FRAC_BITS)
+
+/**
+ * mul_frac() - multiply two fixed-point numbers
+ * @x: first multiplicand
+ * @y: second multiplicand
+ *
+ * Return: the result of multiplying two fixed-point numbers. The
+ * result is also a fixed-point number.
+ */
+static inline s64 mul_frac(s64 x, s64 y)
+{
+ return (x * y) >> FRAC_BITS;
+}
+
+/**
+ * div_frac() - divide two fixed-point numbers
+ * @x: the dividend
+ * @y: the divisor
+ *
+ * Return: the result of dividing two fixed-point numbers. The
+ * result is also a fixed-point number.
+ */
+static inline s64 div_frac(s64 x, s64 y)
+{
+ return div_s64(x << FRAC_BITS, y);
+}
+
+/**
+ * struct power_allocator_params - parameters for the power allocator governor
+ * @err_integral: accumulated error in the PID controller.
+ * @prev_err: error in the previous iteration of the PID controller.
+ * Used to calculate the derivative term.
+ * @trip_switch_on: first passive trip point of the thermal zone. The
+ * governor switches on when this trip point is crossed.
+ * @trip_max_desired_temperature: last passive trip point of the thermal
+ * zone. The temperature we are
+ * controlling for.
+ */
+struct power_allocator_params {
+ s64 err_integral;
+ s32 prev_err;
+ int trip_switch_on;
+ int trip_max_desired_temperature;
+};
+
+/**
+ * pid_controller() - PID controller
+ * @tz: thermal zone we are operating in
+ * @current_temp: the current temperature in millicelsius
+ * @control_temp: the target temperature in millicelsius
+ * @max_allocatable_power: maximum allocatable power for this thermal zone
+ *
+ * This PID controller increases the available power budget so that the
+ * temperature of the thermal zone gets as close as possible to
+ * @control_temp and limits the power if it exceeds it. k_po is the
+ * proportional term when we are overshooting, k_pu is the
+ * proportional term when we are undershooting. integral_cutoff is a
+ * threshold below which we stop accumulating the error. The
+ * accumulated error is only valid if the requested power will make
+ * the system warmer. If the system is mostly idle, there's no point
+ * in accumulating positive error.
+ *
+ * Return: The power budget for the next period.
+ */
+static u32 pid_controller(struct thermal_zone_device *tz,
+ unsigned long current_temp,
+ unsigned long control_temp,
+ u32 max_allocatable_power)
+{
+ s64 p, i, d, power_range;
+ s32 err, max_power_frac;
+ struct power_allocator_params *params = tz->governor_data;
+
+ max_power_frac = int_to_frac(max_allocatable_power);
+
+ err = ((s32)control_temp - (s32)current_temp);
+ err = int_to_frac(err);
+
+ /* Calculate the proportional term */
+ p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err);
+
+ /*
+ * Calculate the integral term
+ *
+ * if the error is less than cut off allow integration (but
+ * the integral is limited to max power)
+ */
+ i = mul_frac(tz->tzp->k_i, params->err_integral);
+
+ if (err < int_to_frac(tz->tzp->integral_cutoff)) {
+ s64 i_next = i + mul_frac(tz->tzp->k_i, err);
+
+ if (abs64(i_next) < max_power_frac) {
+ i = i_next;
+ params->err_integral += err;
+ }
+ }
+
+ /*
+ * Calculate the derivative term
+ *
+ * We do err - prev_err, so with a positive k_d, a decreasing
+ * error (i.e. driving closer to the line) results in less
+ * power being applied, slowing down the controller)
+ */
+ d = mul_frac(tz->tzp->k_d, err - params->prev_err);
+ d = div_frac(d, tz->passive_delay);
+ params->prev_err = err;
+
+ power_range = p + i + d;
+
+ /* feed-forward the known sustainable dissipatable power */
+ power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
+
+ power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
+
+ trace_thermal_power_allocator_pid(tz, frac_to_int(err),
+ frac_to_int(params->err_integral),
+ frac_to_int(p), frac_to_int(i),
+ frac_to_int(d), power_range);
+
+ return power_range;
+}
+
+/**
+ * divvy_up_power() - divvy the allocated power between the actors
+ * @req_power: each actor's requested power
+ * @max_power: each actor's maximum available power
+ * @num_actors: size of the @req_power, @max_power and @granted_power's array
+ * @total_req_power: sum of @req_power
+ * @power_range: total allocated power
+ * @granted_power: output array: each actor's granted power
+ * @extra_actor_power: an appropriately sized array to be used in the
+ * function as temporary storage of the extra power given
+ * to the actors
+ *
+ * This function divides the total allocated power (@power_range)
+ * fairly between the actors. It first tries to give each actor a
+ * share of the @power_range according to how much power it requested
+ * compared to the rest of the actors. For example, if only one actor
+ * requests power, then it receives all the @power_range. If
+ * three actors each requests 1mW, each receives a third of the
+ * @power_range.
+ *
+ * If any actor received more than their maximum power, then that
+ * surplus is re-divvied among the actors based on how far they are
+ * from their respective maximums.
+ *
+ * Granted power for each actor is written to @granted_power, which
+ * should've been allocated by the calling function.
+ */
+static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
+ u32 total_req_power, u32 power_range,
+ u32 *granted_power, u32 *extra_actor_power)
+{
+ u32 extra_power, capped_extra_power;
+ int i;
+
+ /*
+ * Prevent division by 0 if none of the actors request power.
+ */
+ if (!total_req_power)
+ total_req_power = 1;
+
+ capped_extra_power = 0;
+ extra_power = 0;
+ for (i = 0; i < num_actors; i++) {
+ u64 req_range = req_power[i] * power_range;
+
+ granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range,
+ total_req_power);
+
+ if (granted_power[i] > max_power[i]) {
+ extra_power += granted_power[i] - max_power[i];
+ granted_power[i] = max_power[i];
+ }
+
+ extra_actor_power[i] = max_power[i] - granted_power[i];
+ capped_extra_power += extra_actor_power[i];
+ }
+
+ if (!extra_power)
+ return;
+
+ /*
+ * Re-divvy the reclaimed extra among actors based on
+ * how far they are from the max
+ */
+ extra_power = min(extra_power, capped_extra_power);
+ if (capped_extra_power > 0)
+ for (i = 0; i < num_actors; i++)
+ granted_power[i] += (extra_actor_power[i] *
+ extra_power) / capped_extra_power;
+}
+
+static int allocate_power(struct thermal_zone_device *tz,
+ unsigned long current_temp,
+ unsigned long control_temp)
+{
+ struct thermal_instance *instance;
+ struct power_allocator_params *params = tz->governor_data;
+ u32 *req_power, *max_power, *granted_power, *extra_actor_power;
+ u32 total_req_power, max_allocatable_power;
+ u32 total_granted_power, power_range;
+ int i, num_actors, total_weight, ret = 0;
+ int trip_max_desired_temperature = params->trip_max_desired_temperature;
+
+ mutex_lock(&tz->lock);
+
+ num_actors = 0;
+ total_weight = 0;
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ if ((instance->trip == trip_max_desired_temperature) &&
+ cdev_is_power_actor(instance->cdev)) {
+ num_actors++;
+ total_weight += instance->weight;
+ }
+ }
+
+ /*
+ * We need to allocate three arrays of the same size:
+ * req_power, max_power and granted_power. They are going to
+ * be needed until this function returns. Allocate them all
+ * in one go to simplify the allocation and deallocation
+ * logic.
+ */
+ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
+ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
+ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
+ req_power = devm_kcalloc(&tz->device, num_actors * 4,
+ sizeof(*req_power), GFP_KERNEL);
+ if (!req_power) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ max_power = &req_power[num_actors];
+ granted_power = &req_power[2 * num_actors];
+ extra_actor_power = &req_power[3 * num_actors];
+
+ i = 0;
+ total_req_power = 0;
+ max_allocatable_power = 0;
+
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ int weight;
+ struct thermal_cooling_device *cdev = instance->cdev;
+
+ if (instance->trip != trip_max_desired_temperature)
+ continue;
+
+ if (!cdev_is_power_actor(cdev))
+ continue;
+
+ if (cdev->ops->get_requested_power(cdev, tz, &req_power[i]))
+ continue;
+
+ if (!total_weight)
+ weight = 1 << FRAC_BITS;
+ else
+ weight = instance->weight;
+
+ req_power[i] = frac_to_int(weight * req_power[i]);
+
+ if (power_actor_get_max_power(cdev, tz, &max_power[i]))
+ continue;
+
+ total_req_power += req_power[i];
+ max_allocatable_power += max_power[i];
+
+ i++;
+ }
+
+ power_range = pid_controller(tz, current_temp, control_temp,
+ max_allocatable_power);
+
+ divvy_up_power(req_power, max_power, num_actors, total_req_power,
+ power_range, granted_power, extra_actor_power);
+
+ total_granted_power = 0;
+ i = 0;
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ if (instance->trip != trip_max_desired_temperature)
+ continue;
+
+ if (!cdev_is_power_actor(instance->cdev))
+ continue;
+
+ power_actor_set_power(instance->cdev, instance,
+ granted_power[i]);
+ total_granted_power += granted_power[i];
+
+ i++;
+ }
+
+ trace_thermal_power_allocator(tz, req_power, total_req_power,
+ granted_power, total_granted_power,
+ num_actors, power_range,
+ max_allocatable_power, current_temp,
+ (s32)control_temp - (s32)current_temp);
+
+ devm_kfree(&tz->device, req_power);
+unlock:
+ mutex_unlock(&tz->lock);
+
+ return ret;
+}
+
+static int get_governor_trips(struct thermal_zone_device *tz,
+ struct power_allocator_params *params)
+{
+ int i, ret, last_passive;
+ bool found_first_passive;
+
+ found_first_passive = false;
+ last_passive = -1;
+ ret = -EINVAL;
+
+ for (i = 0; i < tz->trips; i++) {
+ enum thermal_trip_type type;
+
+ ret = tz->ops->get_trip_type(tz, i, &type);
+ if (ret)
+ return ret;
+
+ if (!found_first_passive) {
+ if (type == THERMAL_TRIP_PASSIVE) {
+ params->trip_switch_on = i;
+ found_first_passive = true;
+ }
+ } else if (type == THERMAL_TRIP_PASSIVE) {
+ last_passive = i;
+ } else {
+ break;
+ }
+ }
+
+ if (last_passive != -1) {
+ params->trip_max_desired_temperature = last_passive;
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void reset_pid_controller(struct power_allocator_params *params)
+{
+ params->err_integral = 0;
+ params->prev_err = 0;
+}
+
+static void allow_maximum_power(struct thermal_zone_device *tz)
+{
+ struct thermal_instance *instance;
+ struct power_allocator_params *params = tz->governor_data;
+
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+ if ((instance->trip != params->trip_max_desired_temperature) ||
+ (!cdev_is_power_actor(instance->cdev)))
+ continue;
+
+ instance->target = 0;
+ instance->cdev->updated = false;
+ thermal_cdev_update(instance->cdev);
+ }
+}
+
+/**
+ * power_allocator_bind() - bind the power_allocator governor to a thermal zone
+ * @tz: thermal zone to bind it to
+ *
+ * Check that the thermal zone is valid for this governor, that is, it
+ * has two thermal trips. If so, initialize the PID controller
+ * parameters and bind it to the thermal zone.
+ *
+ * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM
+ * if we ran out of memory.
+ */
+static int power_allocator_bind(struct thermal_zone_device *tz)
+{
+ int ret;
+ struct power_allocator_params *params;
+ unsigned long switch_on_temp, control_temp;
+ u32 temperature_threshold;
+
+ if (!tz->tzp || !tz->tzp->sustainable_power) {
+ dev_err(&tz->device,
+ "power_allocator: missing sustainable_power\n");
+ return -EINVAL;
+ }
+
+ params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ ret = get_governor_trips(tz, params);
+ if (ret) {
+ dev_err(&tz->device,
+ "thermal zone %s has wrong trip setup for power allocator\n",
+ tz->type);
+ goto free;
+ }
+
+ ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
+ &switch_on_temp);
+ if (ret)
+ goto free;
+
+ ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
+ &control_temp);
+ if (ret)
+ goto free;
+
+ temperature_threshold = control_temp - switch_on_temp;
+
+ tz->tzp->k_po = tz->tzp->k_po ?:
+ int_to_frac(tz->tzp->sustainable_power) / temperature_threshold;
+ tz->tzp->k_pu = tz->tzp->k_pu ?:
+ int_to_frac(2 * tz->tzp->sustainable_power) /
+ temperature_threshold;
+ tz->tzp->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000;
+ /*
+ * The default for k_d and integral_cutoff is 0, so we can
+ * leave them as they are.
+ */
+
+ reset_pid_controller(params);
+
+ tz->governor_data = params;
+
+ return 0;
+
+free:
+ devm_kfree(&tz->device, params);
+ return ret;
+}
+
+static void power_allocator_unbind(struct thermal_zone_device *tz)
+{
+ dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
+ devm_kfree(&tz->device, tz->governor_data);
+ tz->governor_data = NULL;
+}
+
+static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
+{
+ int ret;
+ unsigned long switch_on_temp, control_temp, current_temp;
+ struct power_allocator_params *params = tz->governor_data;
+
+ /*
+ * We get called for every trip point but we only need to do
+ * our calculations once
+ */
+ if (trip != params->trip_max_desired_temperature)
+ return 0;
+
+ ret = thermal_zone_get_temp(tz, &current_temp);
+ if (ret) {
+ dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
+ return ret;
+ }
+
+ ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
+ &switch_on_temp);
+ if (ret) {
+ dev_warn(&tz->device,
+ "Failed to get switch on temperature: %d\n", ret);
+ return ret;
+ }
+
+ if (current_temp < switch_on_temp) {
+ tz->passive = 0;
+ reset_pid_controller(params);
+ allow_maximum_power(tz);
+ return 0;
+ }
+
+ tz->passive = 1;
+
+ ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
+ &control_temp);
+ if (ret) {
+ dev_warn(&tz->device,
+ "Failed to get the maximum desired temperature: %d\n",
+ ret);
+ return ret;
+ }
+
+ return allocate_power(tz, current_temp, control_temp);
+}
+
+static struct thermal_governor thermal_gov_power_allocator = {
+ .name = "power_allocator",
+ .bind_to_tz = power_allocator_bind,
+ .unbind_from_tz = power_allocator_unbind,
+ .throttle = power_allocator_throttle,
+};
+
+int thermal_gov_power_allocator_register(void)
+{
+ return thermal_register_governor(&thermal_gov_power_allocator);
+}
+
+void thermal_gov_power_allocator_unregister(void)
+{
+ thermal_unregister_governor(&thermal_gov_power_allocator);
+}
diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c
new file mode 100644
index 0000000..c8d27b8
--- /dev/null
+++ b/drivers/thermal/qcom-spmi-temp-alarm.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+
+#define QPNP_TM_REG_TYPE 0x04
+#define QPNP_TM_REG_SUBTYPE 0x05
+#define QPNP_TM_REG_STATUS 0x08
+#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
+#define QPNP_TM_REG_ALARM_CTRL 0x46
+
+#define QPNP_TM_TYPE 0x09
+#define QPNP_TM_SUBTYPE 0x08
+
+#define STATUS_STAGE_MASK 0x03
+
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03
+
+#define ALARM_CTRL_FORCE_ENABLE 0x80
+
+/*
+ * Trip point values based on threshold control
+ * 0 = {105 C, 125 C, 145 C}
+ * 1 = {110 C, 130 C, 150 C}
+ * 2 = {115 C, 135 C, 155 C}
+ * 3 = {120 C, 140 C, 160 C}
+*/
+#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS 2000
+
+#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
+
+#define THRESH_MIN 0
+
+/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
+#define DEFAULT_TEMP 37000
+
+struct qpnp_tm_chip {
+ struct regmap *map;
+ struct thermal_zone_device *tz_dev;
+ long temp;
+ unsigned int thresh;
+ unsigned int stage;
+ unsigned int prev_stage;
+ unsigned int base;
+ struct iio_channel *adc;
+};
+
+static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(chip->map, chip->base + addr, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val;
+ return 0;
+}
+
+static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
+{
+ return regmap_write(chip->map, chip->base + addr, data);
+}
+
+/*
+ * This function updates the internal temp value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ unsigned int stage;
+ int ret;
+ u8 reg = 0;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ stage = reg & STATUS_STAGE_MASK;
+
+ if (stage > chip->stage) {
+ /* increasing stage, use lower bound */
+ chip->temp = (stage - 1) * TEMP_STAGE_STEP +
+ chip->thresh * TEMP_THRESH_STEP +
+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ } else if (stage < chip->stage) {
+ /* decreasing stage, use upper bound */
+ chip->temp = stage * TEMP_STAGE_STEP +
+ chip->thresh * TEMP_THRESH_STEP -
+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ }
+
+ chip->stage = stage;
+
+ return 0;
+}
+
+static int qpnp_tm_get_temp(void *data, long *temp)
+{
+ struct qpnp_tm_chip *chip = data;
+ int ret, mili_celsius;
+
+ if (!temp)
+ return -EINVAL;
+
+ if (IS_ERR(chip->adc)) {
+ ret = qpnp_tm_update_temp_no_adc(chip);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = iio_read_channel_processed(chip->adc, &mili_celsius);
+ if (ret < 0)
+ return ret;
+
+ chip->temp = mili_celsius;
+ }
+
+ *temp = chip->temp < 0 ? 0 : chip->temp;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
+ .get_temp = qpnp_tm_get_temp,
+};
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+
+ thermal_zone_device_update(chip->tz_dev);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function initializes the internal temp value based on only the
+ * current thermal stage and threshold. Setup threshold control and
+ * disable shutdown override.
+ */
+static int qpnp_tm_init(struct qpnp_tm_chip *chip)
+{
+ int ret;
+ u8 reg;
+
+ chip->thresh = THRESH_MIN;
+ chip->temp = DEFAULT_TEMP;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ chip->stage = reg & STATUS_STAGE_MASK;
+
+ if (chip->stage)
+ chip->temp = chip->thresh * TEMP_THRESH_STEP +
+ (chip->stage - 1) * TEMP_STAGE_STEP +
+ TEMP_THRESH_MIN;
+
+ /*
+ * Set threshold and disable software override of stage 2 and 3
+ * shutdowns.
+ */
+ reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Enable the thermal alarm PMIC module in always-on mode. */
+ reg = ALARM_CTRL_FORCE_ENABLE;
+ ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
+
+ return ret;
+}
+
+static int qpnp_tm_probe(struct platform_device *pdev)
+{
+ struct qpnp_tm_chip *chip;
+ struct device_node *node;
+ u8 type, subtype;
+ u32 res[2];
+ int ret, irq;
+
+ node = pdev->dev.of_node;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, chip);
+
+ chip->map = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->map)
+ return -ENXIO;
+
+ ret = of_property_read_u32_array(node, "reg", res, 2);
+ if (ret < 0)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* ADC based measurements are optional */
+ chip->adc = iio_channel_get(&pdev->dev, "thermal");
+ if (PTR_ERR(chip->adc) == -EPROBE_DEFER)
+ return PTR_ERR(chip->adc);
+
+ chip->base = res[0];
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not read type\n");
+ goto fail;
+ }
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not read subtype\n");
+ goto fail;
+ }
+
+ if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
+ dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
+ type, subtype);
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = qpnp_tm_init(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "init failed\n");
+ goto fail;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr,
+ IRQF_ONESHOT, node->name, chip);
+ if (ret < 0)
+ goto fail;
+
+ chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
+ &qpnp_tm_sensor_ops);
+ if (IS_ERR(chip->tz_dev)) {
+ dev_err(&pdev->dev, "failed to register sensor\n");
+ ret = PTR_ERR(chip->tz_dev);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (!IS_ERR(chip->adc))
+ iio_channel_release(chip->adc);
+
+ return ret;
+}
+
+static int qpnp_tm_remove(struct platform_device *pdev)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev);
+ if (!IS_ERR(chip->adc))
+ iio_channel_release(chip->adc);
+
+ return 0;
+}
+
+static const struct of_device_id qpnp_tm_match_table[] = {
+ { .compatible = "qcom,spmi-temp-alarm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qpnp_tm_match_table);
+
+static struct platform_driver qpnp_tm_driver = {
+ .driver = {
+ .name = "spmi-temp-alarm",
+ .of_match_table = qpnp_tm_match_table,
+ },
+ .probe = qpnp_tm_probe,
+ .remove = qpnp_tm_remove,
+};
+module_platform_driver(qpnp_tm_driver);
+
+MODULE_ALIAS("platform:spmi-temp-alarm");
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c
index 1d30b09..531f4b17 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -97,6 +97,32 @@
#define EXYNOS4412_MUX_ADDR_VALUE 6
#define EXYNOS4412_MUX_ADDR_SHIFT 20
+/* Exynos5433 specific registers */
+#define EXYNOS5433_TMU_REG_CONTROL1 0x024
+#define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c
+#define EXYNOS5433_TMU_COUNTER_VALUE0 0x030
+#define EXYNOS5433_TMU_COUNTER_VALUE1 0x034
+#define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044
+#define EXYNOS5433_THD_TEMP_RISE3_0 0x050
+#define EXYNOS5433_THD_TEMP_RISE7_4 0x054
+#define EXYNOS5433_THD_TEMP_FALL3_0 0x060
+#define EXYNOS5433_THD_TEMP_FALL7_4 0x064
+#define EXYNOS5433_TMU_REG_INTEN 0x0c0
+#define EXYNOS5433_TMU_REG_INTPEND 0x0c8
+#define EXYNOS5433_TMU_EMUL_CON 0x110
+#define EXYNOS5433_TMU_PD_DET_EN 0x130
+
+#define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16
+#define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23
+#define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \
+ (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT)
+#define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23)
+
+#define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0
+#define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1
+
+#define EXYNOS5433_PD_DET_EN 1
+
/*exynos5440 specific registers*/
#define EXYNOS5440_TMU_S0_7_TRIM 0x000
#define EXYNOS5440_TMU_S0_7_CTRL 0x020
@@ -484,6 +510,101 @@ out:
return ret;
}
+static int exynos5433_tmu_initialize(struct platform_device *pdev)
+{
+ struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct exynos_tmu_platform_data *pdata = data->pdata;
+ struct thermal_zone_device *tz = data->tzd;
+ unsigned int status, trim_info;
+ unsigned int rising_threshold = 0, falling_threshold = 0;
+ unsigned long temp, temp_hist;
+ int ret = 0, threshold_code, i, sensor_id, cal_type;
+
+ status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+ if (!status) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+ sanitize_temp_error(data, trim_info);
+
+ /* Read the temperature sensor id */
+ sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK)
+ >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT;
+ dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id);
+
+ /* Read the calibration mode */
+ writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO);
+ cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK)
+ >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT;
+
+ switch (cal_type) {
+ case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING:
+ pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
+ break;
+ case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING:
+ pdata->cal_type = TYPE_TWO_POINT_TRIMMING;
+ break;
+ default:
+ pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
+ break;
+ };
+
+ dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
+ cal_type ? 2 : 1);
+
+ /* Write temperature code for rising and falling threshold */
+ for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
+ int rising_reg_offset, falling_reg_offset;
+ int j = 0;
+
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0;
+ falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0;
+ j = i;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4;
+ falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4;
+ j = i - 4;
+ break;
+ default:
+ continue;
+ }
+
+ /* Write temperature code for rising threshold */
+ tz->ops->get_trip_temp(tz, i, &temp);
+ temp /= MCELSIUS;
+ threshold_code = temp_to_code(data, temp);
+
+ rising_threshold = readl(data->base + rising_reg_offset);
+ rising_threshold |= (threshold_code << j * 8);
+ writel(rising_threshold, data->base + rising_reg_offset);
+
+ /* Write temperature code for falling threshold */
+ tz->ops->get_trip_hyst(tz, i, &temp_hist);
+ temp_hist = temp - (temp_hist / MCELSIUS);
+ threshold_code = temp_to_code(data, temp_hist);
+
+ falling_threshold = readl(data->base + falling_reg_offset);
+ falling_threshold &= ~(0xff << j * 8);
+ falling_threshold |= (threshold_code << j * 8);
+ writel(falling_threshold, data->base + falling_reg_offset);
+ }
+
+ data->tmu_clear_irqs(data);
+out:
+ return ret;
+}
+
static int exynos5440_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -643,6 +764,48 @@ static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
+static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
+{
+ struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+ struct thermal_zone_device *tz = data->tzd;
+ unsigned int con, interrupt_en, pd_det_en;
+
+ con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
+
+ if (on) {
+ con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ interrupt_en =
+ (of_thermal_is_trip_valid(tz, 7)
+ << EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 6)
+ << EXYNOS7_TMU_INTEN_RISE6_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 5)
+ << EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 4)
+ << EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 3)
+ << EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 2)
+ << EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 1)
+ << EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
+ (of_thermal_is_trip_valid(tz, 0)
+ << EXYNOS7_TMU_INTEN_RISE0_SHIFT);
+
+ interrupt_en |=
+ interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
+ } else {
+ con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ interrupt_en = 0; /* Disable all interrupts */
+ }
+
+ pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0;
+
+ writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN);
+ writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN);
+ writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+}
+
static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
@@ -770,6 +933,8 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
if (data->soc == SOC_ARCH_EXYNOS5260)
emul_con = EXYNOS5260_EMUL_CON;
+ if (data->soc == SOC_ARCH_EXYNOS5433)
+ emul_con = EXYNOS5433_TMU_EMUL_CON;
else if (data->soc == SOC_ARCH_EXYNOS7)
emul_con = EXYNOS7_TMU_REG_EMUL_CON;
else
@@ -882,6 +1047,9 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
} else if (data->soc == SOC_ARCH_EXYNOS7) {
tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
+ } else if (data->soc == SOC_ARCH_EXYNOS5433) {
+ tmu_intstat = EXYNOS5433_TMU_REG_INTPEND;
+ tmu_intclear = EXYNOS5433_TMU_REG_INTPEND;
} else {
tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
@@ -926,6 +1094,7 @@ static const struct of_device_id exynos_tmu_match[] = {
{ .compatible = "samsung,exynos5260-tmu", },
{ .compatible = "samsung,exynos5420-tmu", },
{ .compatible = "samsung,exynos5420-tmu-ext-triminfo", },
+ { .compatible = "samsung,exynos5433-tmu", },
{ .compatible = "samsung,exynos5440-tmu", },
{ .compatible = "samsung,exynos7-tmu", },
{ /* sentinel */ },
@@ -949,6 +1118,8 @@ static int exynos_of_get_soc_type(struct device_node *np)
else if (of_device_is_compatible(np,
"samsung,exynos5420-tmu-ext-triminfo"))
return SOC_ARCH_EXYNOS5420_TRIMINFO;
+ else if (of_device_is_compatible(np, "samsung,exynos5433-tmu"))
+ return SOC_ARCH_EXYNOS5433;
else if (of_device_is_compatible(np, "samsung,exynos5440-tmu"))
return SOC_ARCH_EXYNOS5440;
else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
@@ -1069,6 +1240,13 @@ static int exynos_map_dt_data(struct platform_device *pdev)
data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
break;
+ case SOC_ARCH_EXYNOS5433:
+ data->tmu_initialize = exynos5433_tmu_initialize;
+ data->tmu_control = exynos5433_tmu_control;
+ data->tmu_read = exynos4412_tmu_read;
+ data->tmu_set_emulation = exynos4412_tmu_set_emulation;
+ data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
+ break;
case SOC_ARCH_EXYNOS5440:
data->tmu_initialize = exynos5440_tmu_initialize;
data->tmu_control = exynos5440_tmu_control;
@@ -1172,7 +1350,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk_sec;
}
- if (data->soc == SOC_ARCH_EXYNOS7) {
+ switch (data->soc) {
+ case SOC_ARCH_EXYNOS5433:
+ case SOC_ARCH_EXYNOS7:
data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
if (IS_ERR(data->sclk)) {
dev_err(&pdev->dev, "Failed to get sclk\n");
@@ -1184,7 +1364,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk;
}
}
- }
+ break;
+ default:
+ break;
+ };
ret = exynos_tmu_initialize(pdev);
if (ret) {
diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h
index 4d71ec6..440c714 100644
--- a/drivers/thermal/samsung/exynos_tmu.h
+++ b/drivers/thermal/samsung/exynos_tmu.h
@@ -33,6 +33,7 @@ enum soc_type {
SOC_ARCH_EXYNOS5260,
SOC_ARCH_EXYNOS5420,
SOC_ARCH_EXYNOS5420_TRIMINFO,
+ SOC_ARCH_EXYNOS5433,
SOC_ARCH_EXYNOS5440,
SOC_ARCH_EXYNOS7,
};
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 4108db7..04659bf 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -75,6 +75,58 @@ static struct thermal_governor *__find_governor(const char *name)
return NULL;
}
+/**
+ * bind_previous_governor() - bind the previous governor of the thermal zone
+ * @tz: a valid pointer to a struct thermal_zone_device
+ * @failed_gov_name: the name of the governor that failed to register
+ *
+ * Register the previous governor of the thermal zone after a new
+ * governor has failed to be bound.
+ */
+static void bind_previous_governor(struct thermal_zone_device *tz,
+ const char *failed_gov_name)
+{
+ if (tz->governor && tz->governor->bind_to_tz) {
+ if (tz->governor->bind_to_tz(tz)) {
+ dev_err(&tz->device,
+ "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n",
+ failed_gov_name, tz->governor->name, tz->type);
+ tz->governor = NULL;
+ }
+ }
+}
+
+/**
+ * thermal_set_governor() - Switch to another governor
+ * @tz: a valid pointer to a struct thermal_zone_device
+ * @new_gov: pointer to the new governor
+ *
+ * Change the governor of thermal zone @tz.
+ *
+ * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
+ */
+static int thermal_set_governor(struct thermal_zone_device *tz,
+ struct thermal_governor *new_gov)
+{
+ int ret = 0;
+
+ if (tz->governor && tz->governor->unbind_from_tz)
+ tz->governor->unbind_from_tz(tz);
+
+ if (new_gov && new_gov->bind_to_tz) {
+ ret = new_gov->bind_to_tz(tz);
+ if (ret) {
+ bind_previous_governor(tz, new_gov->name);
+
+ return ret;
+ }
+ }
+
+ tz->governor = new_gov;
+
+ return ret;
+}
+
int thermal_register_governor(struct thermal_governor *governor)
{
int err;
@@ -107,8 +159,15 @@ int thermal_register_governor(struct thermal_governor *governor)
name = pos->tzp->governor_name;
- if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH))
- pos->governor = governor;
+ if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
+ int ret;
+
+ ret = thermal_set_governor(pos, governor);
+ if (ret)
+ dev_err(&pos->device,
+ "Failed to set governor %s for thermal zone %s: %d\n",
+ governor->name, pos->type, ret);
+ }
}
mutex_unlock(&thermal_list_lock);
@@ -134,7 +193,7 @@ void thermal_unregister_governor(struct thermal_governor *governor)
list_for_each_entry(pos, &thermal_tz_list, node) {
if (!strncasecmp(pos->governor->name, governor->name,
THERMAL_NAME_LENGTH))
- pos->governor = NULL;
+ thermal_set_governor(pos, NULL);
}
mutex_unlock(&thermal_list_lock);
@@ -218,7 +277,8 @@ static void print_bind_err_msg(struct thermal_zone_device *tz,
static void __bind(struct thermal_zone_device *tz, int mask,
struct thermal_cooling_device *cdev,
- unsigned long *limits)
+ unsigned long *limits,
+ unsigned int weight)
{
int i, ret;
@@ -233,7 +293,8 @@ static void __bind(struct thermal_zone_device *tz, int mask,
upper = limits[i * 2 + 1];
}
ret = thermal_zone_bind_cooling_device(tz, i, cdev,
- upper, lower);
+ upper, lower,
+ weight);
if (ret)
print_bind_err_msg(tz, cdev, ret);
}
@@ -280,7 +341,8 @@ static void bind_cdev(struct thermal_cooling_device *cdev)
continue;
tzp->tbp[i].cdev = cdev;
__bind(pos, tzp->tbp[i].trip_mask, cdev,
- tzp->tbp[i].binding_limits);
+ tzp->tbp[i].binding_limits,
+ tzp->tbp[i].weight);
}
}
@@ -319,7 +381,8 @@ static void bind_tz(struct thermal_zone_device *tz)
continue;
tzp->tbp[i].cdev = pos;
__bind(tz, tzp->tbp[i].trip_mask, pos,
- tzp->tbp[i].binding_limits);
+ tzp->tbp[i].binding_limits,
+ tzp->tbp[i].weight);
}
}
exit:
@@ -713,7 +776,8 @@ passive_store(struct device *dev, struct device_attribute *attr,
thermal_zone_bind_cooling_device(tz,
THERMAL_TRIPS_NONE, cdev,
THERMAL_NO_LIMIT,
- THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
}
mutex_unlock(&thermal_list_lock);
if (!tz->passive_delay)
@@ -765,8 +829,9 @@ policy_store(struct device *dev, struct device_attribute *attr,
if (!gov)
goto exit;
- tz->governor = gov;
- ret = count;
+ ret = thermal_set_governor(tz, gov);
+ if (!ret)
+ ret = count;
exit:
mutex_unlock(&tz->lock);
@@ -810,6 +875,158 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
#endif/*CONFIG_THERMAL_EMULATION*/
+static ssize_t
+sustainable_power_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+
+ if (tz->tzp)
+ return sprintf(buf, "%u\n", tz->tzp->sustainable_power);
+ else
+ return -EIO;
+}
+
+static ssize_t
+sustainable_power_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+ u32 sustainable_power;
+
+ if (!tz->tzp)
+ return -EIO;
+
+ if (kstrtou32(buf, 10, &sustainable_power))
+ return -EINVAL;
+
+ tz->tzp->sustainable_power = sustainable_power;
+
+ return count;
+}
+static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show,
+ sustainable_power_store);
+
+#define create_s32_tzp_attr(name) \
+ static ssize_t \
+ name##_show(struct device *dev, struct device_attribute *devattr, \
+ char *buf) \
+ { \
+ struct thermal_zone_device *tz = to_thermal_zone(dev); \
+ \
+ if (tz->tzp) \
+ return sprintf(buf, "%u\n", tz->tzp->name); \
+ else \
+ return -EIO; \
+ } \
+ \
+ static ssize_t \
+ name##_store(struct device *dev, struct device_attribute *devattr, \
+ const char *buf, size_t count) \
+ { \
+ struct thermal_zone_device *tz = to_thermal_zone(dev); \
+ s32 value; \
+ \
+ if (!tz->tzp) \
+ return -EIO; \
+ \
+ if (kstrtos32(buf, 10, &value)) \
+ return -EINVAL; \
+ \
+ tz->tzp->name = value; \
+ \
+ return count; \
+ } \
+ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store)
+
+create_s32_tzp_attr(k_po);
+create_s32_tzp_attr(k_pu);
+create_s32_tzp_attr(k_i);
+create_s32_tzp_attr(k_d);
+create_s32_tzp_attr(integral_cutoff);
+create_s32_tzp_attr(slope);
+create_s32_tzp_attr(offset);
+#undef create_s32_tzp_attr
+
+static struct device_attribute *dev_tzp_attrs[] = {
+ &dev_attr_sustainable_power,
+ &dev_attr_k_po,
+ &dev_attr_k_pu,
+ &dev_attr_k_i,
+ &dev_attr_k_d,
+ &dev_attr_integral_cutoff,
+ &dev_attr_slope,
+ &dev_attr_offset,
+};
+
+static int create_tzp_attrs(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) {
+ int ret;
+ struct device_attribute *dev_attr = dev_tzp_attrs[i];
+
+ ret = device_create_file(dev, dev_attr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * power_actor_get_max_power() - get the maximum power that a cdev can consume
+ * @cdev: pointer to &thermal_cooling_device
+ * @tz: a valid thermal zone device pointer
+ * @max_power: pointer in which to store the maximum power
+ *
+ * Calculate the maximum power consumption in milliwats that the
+ * cooling device can currently consume and store it in @max_power.
+ *
+ * Return: 0 on success, -EINVAL if @cdev doesn't support the
+ * power_actor API or -E* on other error.
+ */
+int power_actor_get_max_power(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, u32 *max_power)
+{
+ if (!cdev_is_power_actor(cdev))
+ return -EINVAL;
+
+ return cdev->ops->state2power(cdev, tz, 0, max_power);
+}
+
+/**
+ * power_actor_set_power() - limit the maximum power that a cooling device can consume
+ * @cdev: pointer to &thermal_cooling_device
+ * @instance: thermal instance to update
+ * @power: the power in milliwatts
+ *
+ * Set the cooling device to consume at most @power milliwatts.
+ *
+ * Return: 0 on success, -EINVAL if the cooling device does not
+ * implement the power actor API or -E* for other failures.
+ */
+int power_actor_set_power(struct thermal_cooling_device *cdev,
+ struct thermal_instance *instance, u32 power)
+{
+ unsigned long state;
+ int ret;
+
+ if (!cdev_is_power_actor(cdev))
+ return -EINVAL;
+
+ ret = cdev->ops->power2state(cdev, instance->tz, power, &state);
+ if (ret)
+ return ret;
+
+ instance->target = state;
+ cdev->updated = false;
+ thermal_cdev_update(cdev);
+
+ return 0;
+}
+
static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
@@ -917,6 +1134,34 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
NULL,
};
+static ssize_t
+thermal_cooling_device_weight_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_instance *instance;
+
+ instance = container_of(attr, struct thermal_instance, weight_attr);
+
+ return sprintf(buf, "%d\n", instance->weight);
+}
+
+static ssize_t
+thermal_cooling_device_weight_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_instance *instance;
+ int ret, weight;
+
+ ret = kstrtoint(buf, 0, &weight);
+ if (ret)
+ return ret;
+
+ instance = container_of(attr, struct thermal_instance, weight_attr);
+ instance->weight = weight;
+
+ return count;
+}
/* Device management */
/**
@@ -931,6 +1176,9 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
* @lower: the Minimum cooling state can be used for this trip point.
* THERMAL_NO_LIMIT means no lower limit,
* and the cooling device can be in cooling state 0.
+ * @weight: The weight of the cooling device to be bound to the
+ * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the
+ * default value
*
* This interface function bind a thermal cooling device to the certain trip
* point of a thermal zone device.
@@ -941,7 +1189,8 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
int trip,
struct thermal_cooling_device *cdev,
- unsigned long upper, unsigned long lower)
+ unsigned long upper, unsigned long lower,
+ unsigned int weight)
{
struct thermal_instance *dev;
struct thermal_instance *pos;
@@ -986,6 +1235,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
dev->upper = upper;
dev->lower = lower;
dev->target = THERMAL_NO_TARGET;
+ dev->weight = weight;
result = get_idr(&tz->idr, &tz->lock, &dev->id);
if (result)
@@ -1006,6 +1256,16 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (result)
goto remove_symbol_link;
+ sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);
+ sysfs_attr_init(&dev->weight_attr.attr);
+ dev->weight_attr.attr.name = dev->weight_attr_name;
+ dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;
+ dev->weight_attr.show = thermal_cooling_device_weight_show;
+ dev->weight_attr.store = thermal_cooling_device_weight_store;
+ result = device_create_file(&tz->device, &dev->weight_attr);
+ if (result)
+ goto remove_trip_file;
+
mutex_lock(&tz->lock);
mutex_lock(&cdev->lock);
list_for_each_entry(pos, &tz->thermal_instances, tz_node)
@@ -1023,6 +1283,8 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (!result)
return 0;
+ device_remove_file(&tz->device, &dev->weight_attr);
+remove_trip_file:
device_remove_file(&tz->device, &dev->attr);
remove_symbol_link:
sysfs_remove_link(&tz->device.kobj, dev->name);
@@ -1377,7 +1639,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
tz->trip_temp_attrs[indx].name;
tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
- if (mask & (1 << indx)) {
+ if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) &&
+ mask & (1 << indx)) {
tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
tz->trip_temp_attrs[indx].attr.store =
trip_point_temp_store;
@@ -1454,7 +1717,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
struct thermal_zone_device *thermal_zone_device_register(const char *type,
int trips, int mask, void *devdata,
struct thermal_zone_device_ops *ops,
- const struct thermal_zone_params *tzp,
+ struct thermal_zone_params *tzp,
int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz;
@@ -1462,6 +1725,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
int result;
int count;
int passive = 0;
+ struct thermal_governor *governor;
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
@@ -1548,13 +1812,24 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
if (result)
goto unregister;
+ /* Add thermal zone params */
+ result = create_tzp_attrs(&tz->device);
+ if (result)
+ goto unregister;
+
/* Update 'this' zone's governor information */
mutex_lock(&thermal_governor_lock);
if (tz->tzp)
- tz->governor = __find_governor(tz->tzp->governor_name);
+ governor = __find_governor(tz->tzp->governor_name);
else
- tz->governor = def_governor;
+ governor = def_governor;
+
+ result = thermal_set_governor(tz, governor);
+ if (result) {
+ mutex_unlock(&thermal_governor_lock);
+ goto unregister;
+ }
mutex_unlock(&thermal_governor_lock);
@@ -1643,7 +1918,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
device_remove_file(&tz->device, &dev_attr_mode);
device_remove_file(&tz->device, &dev_attr_policy);
remove_trip_attrs(tz);
- tz->governor = NULL;
+ thermal_set_governor(tz, NULL);
thermal_remove_hwmon_sysfs(tz);
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
@@ -1799,7 +2074,11 @@ static int __init thermal_register_governors(void)
if (result)
return result;
- return thermal_gov_user_space_register();
+ result = thermal_gov_user_space_register();
+ if (result)
+ return result;
+
+ return thermal_gov_power_allocator_register();
}
static void thermal_unregister_governors(void)
@@ -1808,6 +2087,7 @@ static void thermal_unregister_governors(void)
thermal_gov_fair_share_unregister();
thermal_gov_bang_bang_unregister();
thermal_gov_user_space_unregister();
+ thermal_gov_power_allocator_unregister();
}
static int __init thermal_init(void)
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 8e39181..d7ac1fc 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -46,8 +46,11 @@ struct thermal_instance {
unsigned long target; /* expected cooling state */
char attr_name[THERMAL_NAME_LENGTH];
struct device_attribute attr;
+ char weight_attr_name[THERMAL_NAME_LENGTH];
+ struct device_attribute weight_attr;
struct list_head tz_node; /* node in tz->thermal_instances */
struct list_head cdev_node; /* node in cdev->thermal_instances */
+ unsigned int weight; /* The weight of the cooling device */
};
int thermal_register_governor(struct thermal_governor *);
@@ -85,6 +88,14 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
static inline void thermal_gov_user_space_unregister(void) {}
#endif /* CONFIG_THERMAL_GOV_USER_SPACE */
+#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
+int thermal_gov_power_allocator_register(void);
+void thermal_gov_power_allocator_unregister(void);
+#else
+static inline int thermal_gov_power_allocator_register(void) { return 0; }
+static inline void thermal_gov_power_allocator_unregister(void) {}
+#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
+
/* device tree support */
#ifdef CONFIG_THERMAL_OF
int of_parse_thermal_zones(void);
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index bc14dc8..10c47c0 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -43,6 +43,8 @@
#include "ti-bandgap.h"
+static int ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id);
+
/*** Helper functions to access registers and their bitfields ***/
/**
@@ -103,19 +105,15 @@ do { \
*/
static int ti_bandgap_power(struct ti_bandgap *bgp, bool on)
{
- int i, ret = 0;
+ int i;
- if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH)) {
- ret = -ENOTSUPP;
- goto exit;
- }
+ if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH))
+ return -ENOTSUPP;
for (i = 0; i < bgp->conf->sensor_count; i++)
/* active on 0 */
RMW_BITS(bgp, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on);
-
-exit:
- return ret;
+ return 0;
}
/**
@@ -298,18 +296,13 @@ static
int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t)
{
const struct ti_bandgap_data *conf = bgp->conf;
- int ret = 0;
/* look up for temperature in the table and return the temperature */
- if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) {
- ret = -ERANGE;
- goto exit;
- }
+ if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val)
+ return -ERANGE;
*t = bgp->conf->conv_table[adc_val - conf->adc_start_val];
-
-exit:
- return ret;
+ return 0;
}
/**
@@ -330,16 +323,14 @@ int ti_bandgap_mcelsius_to_adc(struct ti_bandgap *bgp, long temp, int *adc)
{
const struct ti_bandgap_data *conf = bgp->conf;
const int *conv_table = bgp->conf->conv_table;
- int high, low, mid, ret = 0;
+ int high, low, mid;
low = 0;
high = conf->adc_end_val - conf->adc_start_val;
mid = (high + low) / 2;
- if (temp < conv_table[low] || temp > conv_table[high]) {
- ret = -ERANGE;
- goto exit;
- }
+ if (temp < conv_table[low] || temp > conv_table[high])
+ return -ERANGE;
while (low < high) {
if (temp < conv_table[mid])
@@ -350,9 +341,7 @@ int ti_bandgap_mcelsius_to_adc(struct ti_bandgap *bgp, long temp, int *adc)
}
*adc = conf->adc_start_val + low;
-
-exit:
- return ret;
+ return 0;
}
/**
@@ -378,13 +367,11 @@ int ti_bandgap_add_hyst(struct ti_bandgap *bgp, int adc_val, int hyst_val,
*/
ret = ti_bandgap_adc_to_mcelsius(bgp, adc_val, &temp);
if (ret < 0)
- goto exit;
+ return ret;
temp += hyst_val;
ret = ti_bandgap_mcelsius_to_adc(bgp, temp, sum);
-
-exit:
return ret;
}
@@ -542,22 +529,18 @@ exit:
*/
static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id)
{
- int ret = 0;
-
if (!bgp || IS_ERR(bgp)) {
pr_err("%s: invalid bandgap pointer\n", __func__);
- ret = -EINVAL;
- goto exit;
+ return -EINVAL;
}
if ((id < 0) || (id >= bgp->conf->sensor_count)) {
dev_err(bgp->dev, "%s: sensor id out of range (%d)\n",
__func__, id);
- ret = -ERANGE;
+ return -ERANGE;
}
-exit:
- return ret;
+ return 0;
}
/**
@@ -585,12 +568,10 @@ static int _ti_bandgap_write_threshold(struct ti_bandgap *bgp, int id, int val,
ret = ti_bandgap_validate(bgp, id);
if (ret)
- goto exit;
+ return ret;
- if (!TI_BANDGAP_HAS(bgp, TALERT)) {
- ret = -ENOTSUPP;
- goto exit;
- }
+ if (!TI_BANDGAP_HAS(bgp, TALERT))
+ return -ENOTSUPP;
ts_data = bgp->conf->sensors[id].ts_data;
tsr = bgp->conf->sensors[id].registers;
@@ -603,17 +584,15 @@ static int _ti_bandgap_write_threshold(struct ti_bandgap *bgp, int id, int val,
}
if (ret)
- goto exit;
+ return ret;
ret = ti_bandgap_mcelsius_to_adc(bgp, val, &adc_val);
if (ret < 0)
- goto exit;
+ return ret;
spin_lock(&bgp->lock);
ret = ti_bandgap_update_alert_threshold(bgp, id, adc_val, hot);
spin_unlock(&bgp->lock);
-
-exit:
return ret;
}
@@ -656,7 +635,7 @@ static int _ti_bandgap_read_threshold(struct ti_bandgap *bgp, int id,
temp = ti_bandgap_readl(bgp, tsr->bgap_threshold);
temp = (temp & mask) >> __ffs(mask);
- ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
+ ret = ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
if (ret) {
dev_err(bgp->dev, "failed to read thot\n");
ret = -EIO;
@@ -926,11 +905,17 @@ int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id,
if (ret)
return ret;
+ if (!TI_BANDGAP_HAS(bgp, MODE_CONFIG)) {
+ ret = ti_bandgap_force_single_read(bgp, id);
+ if (ret)
+ return ret;
+ }
+
spin_lock(&bgp->lock);
temp = ti_bandgap_read_temp(bgp, id);
spin_unlock(&bgp->lock);
- ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
+ ret = ti_bandgap_adc_to_mcelsius(bgp, temp, &temp);
if (ret)
return -EIO;
@@ -991,7 +976,8 @@ void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id)
static int
ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
{
- u32 temp = 0, counter = 1000;
+ u32 counter = 1000;
+ struct temp_sensor_registers *tsr;
/* Select single conversion mode */
if (TI_BANDGAP_HAS(bgp, MODE_CONFIG))
@@ -999,16 +985,27 @@ ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id)
/* Start of Conversion = 1 */
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1);
- /* Wait until DTEMP is updated */
- temp = ti_bandgap_read_temp(bgp, id);
- while ((temp == 0) && --counter)
- temp = ti_bandgap_read_temp(bgp, id);
- /* REVISIT: Check correct condition for end of conversion */
+ /* Wait for EOCZ going up */
+ tsr = bgp->conf->sensors[id].registers;
+
+ while (--counter) {
+ if (ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) &
+ tsr->bgap_eocz_mask)
+ break;
+ }
/* Start of Conversion = 0 */
RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0);
+ /* Wait for EOCZ going down */
+ counter = 1000;
+ while (--counter) {
+ if (!(ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) &
+ tsr->bgap_eocz_mask))
+ break;
+ }
+
return 0;
}
@@ -1294,11 +1291,10 @@ int ti_bandgap_probe(struct platform_device *pdev)
goto free_irqs;
}
- bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name);
+ bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name);
ret = IS_ERR(bgp->div_clk);
if (ret) {
- dev_err(&pdev->dev,
- "failed to request div_ts_ck clock ref\n");
+ dev_err(&pdev->dev, "failed to request div_ts_ck clock ref\n");
ret = PTR_ERR(bgp->div_clk);
goto free_irqs;
}
diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
index a38c175..c7c5b37 100644
--- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
+++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
@@ -75,7 +75,7 @@ static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
}
/* thermal zone ops */
-/* Get temperature callback function for thermal zone*/
+/* Get temperature callback function for thermal zone */
static inline int __ti_thermal_get_temp(void *devdata, long *temp)
{
struct thermal_zone_device *pcb_tz = NULL;
@@ -146,7 +146,8 @@ static int ti_thermal_bind(struct thermal_zone_device *thermal,
return thermal_zone_bind_cooling_device(thermal, 0, cdev,
/* bind with min and max states defined by cpu_cooling */
THERMAL_NO_LIMIT,
- THERMAL_NO_LIMIT);
+ THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
}
/* Unbind callback functions for thermal zone */
diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c
index 9ea3d9d..50d1d2c 100644
--- a/drivers/thermal/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -68,7 +68,7 @@ struct phy_dev_entry {
struct thermal_zone_device *tzone;
};
-static const struct thermal_zone_params pkg_temp_tz_params = {
+static struct thermal_zone_params pkg_temp_tz_params = {
.no_hwmon = true,
};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f8120c1..dea1eff 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -728,7 +728,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+ depends on SUPERH || ARCH_SHMOBILE || H8300 || COMPILE_TEST
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index e7d6566..95772cf 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -84,7 +84,7 @@ struct sci_port {
int overrun_bit;
unsigned int error_mask;
unsigned int sampling_rate;
-
+ resource_size_t reg_size;
/* Break timer */
struct timer_list break_timer;
@@ -2073,23 +2073,9 @@ static const char *sci_type(struct uart_port *port)
return NULL;
}
-static inline unsigned long sci_port_size(struct uart_port *port)
-{
- /*
- * Pick an arbitrary size that encapsulates all of the base
- * registers by default. This can be optimized later, or derived
- * from platform resource data at such a time that ports begin to
- * behave more erratically.
- */
- if (port->type == PORT_HSCIF)
- return 96;
- else
- return 64;
-}
-
static int sci_remap_port(struct uart_port *port)
{
- unsigned long size = sci_port_size(port);
+ struct sci_port *sport = to_sci_port(port);
/*
* Nothing to do if there's already an established membase.
@@ -2098,7 +2084,7 @@ static int sci_remap_port(struct uart_port *port)
return 0;
if (port->flags & UPF_IOREMAP) {
- port->membase = ioremap_nocache(port->mapbase, size);
+ port->membase = ioremap_nocache(port->mapbase, sport->reg_size);
if (unlikely(!port->membase)) {
dev_err(port->dev, "can't remap port#%d\n", port->line);
return -ENXIO;
@@ -2117,23 +2103,28 @@ static int sci_remap_port(struct uart_port *port)
static void sci_release_port(struct uart_port *port)
{
+ struct sci_port *sport = to_sci_port(port);
+
if (port->flags & UPF_IOREMAP) {
iounmap(port->membase);
port->membase = NULL;
}
- release_mem_region(port->mapbase, sci_port_size(port));
+ release_mem_region(port->mapbase, sport->reg_size);
}
static int sci_request_port(struct uart_port *port)
{
- unsigned long size = sci_port_size(port);
struct resource *res;
+ struct sci_port *sport = to_sci_port(port);
int ret;
- res = request_mem_region(port->mapbase, size, dev_name(port->dev));
- if (unlikely(res == NULL))
+ res = request_mem_region(port->mapbase, sport->reg_size,
+ dev_name(port->dev));
+ if (unlikely(res == NULL)) {
+ dev_err(port->dev, "request_mem_region failed.");
return -EBUSY;
+ }
ret = sci_remap_port(port);
if (unlikely(ret != 0)) {
@@ -2207,6 +2198,7 @@ static int sci_init_single(struct platform_device *dev,
return -ENOMEM;
port->mapbase = res->start;
+ sci_port->reg_size = resource_size(res);
for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i)
sci_port->irqs[i] = platform_get_irq(dev, i);
@@ -2536,6 +2528,12 @@ static const struct of_device_id of_sci_match[] = {
.regtype = SCIx_HSCIF_REGTYPE,
},
}, {
+ .compatible = "renesas,sci",
+ .data = &(const struct sci_port_info) {
+ .type = PORT_SCI,
+ .regtype = SCIx_SCI_REGTYPE,
+ },
+ }, {
/* Terminator */
},
};
OpenPOWER on IntegriCloud