summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2012-09-07 15:07:55 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-09-07 15:07:55 -0400
commitfac805f8c198092de9a2842efd7f5022e2937b18 (patch)
tree7557809c373f97a343c427d8fded0696060394ce /drivers
parent2461c7d60f9f3821274e4acf9019cba8b82c94b5 (diff)
parentf10723841e624c0726c70356b31d91befed01dd6 (diff)
downloadlinux-fac805f8c198092de9a2842efd7f5022e2937b18.tar.bz2
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig4
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/video_detect.c60
-rw-r--r--drivers/ata/pata_arasan_cf.c14
-rw-r--r--drivers/base/Kconfig1
-rw-r--r--drivers/base/devtmpfs.c9
-rw-r--r--drivers/base/dma-mapping.c49
-rw-r--r--drivers/bcma/host_pci.c1
-rw-r--r--drivers/bcma/sprom.c4
-rw-r--r--drivers/block/drbd/drbd_actlog.c8
-rw-r--r--drivers/block/drbd/drbd_bitmap.c4
-rw-r--r--drivers/block/drbd/drbd_int.h44
-rw-r--r--drivers/block/drbd/drbd_main.c65
-rw-r--r--drivers/block/drbd/drbd_nl.c36
-rw-r--r--drivers/block/drbd/drbd_proc.c3
-rw-r--r--drivers/block/drbd/drbd_receiver.c38
-rw-r--r--drivers/block/drbd/drbd_req.c9
-rw-r--r--drivers/block/drbd/drbd_worker.c12
-rw-r--r--drivers/block/floppy.c24
-rw-r--r--drivers/block/nbd.c8
-rw-r--r--drivers/block/rbd.c816
-rw-r--r--drivers/block/rbd_types.h1
-rw-r--r--drivers/block/umem.c37
-rw-r--r--drivers/block/virtio_blk.c115
-rw-r--r--drivers/block/xen-blkfront.c5
-rw-r--r--drivers/bluetooth/ath3k.c4
-rw-r--r--drivers/bluetooth/btusb.c12
-rw-r--r--drivers/char/hw_random/Kconfig14
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/bcm63xx-rng.c175
-rw-r--r--drivers/char/hw_random/virtio-rng.c37
-rw-r--r--drivers/char/mspec.c2
-rw-r--r--drivers/char/random.c355
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/clk.c3
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c55
-rw-r--r--drivers/crypto/n2_core.c3
-rw-r--r--drivers/dma/Kconfig11
-rw-r--r--drivers/dma/Makefile2
-rw-r--r--drivers/dma/amba-pl08x.c941
-rw-r--r--drivers/dma/omap-dma.c669
-rw-r--r--drivers/dma/sa11x0-dma.c388
-rw-r--r--drivers/dma/virt-dma.c123
-rw-r--r--drivers/dma/virt-dma.h152
-rw-r--r--drivers/edac/Kconfig24
-rw-r--r--drivers/edac/Makefile3
-rw-r--r--drivers/edac/amd64_edac.c376
-rw-r--r--drivers/edac/amd64_edac.h29
-rw-r--r--drivers/edac/amd64_edac_dbg.c89
-rw-r--r--drivers/edac/amd64_edac_inj.c134
-rw-r--r--drivers/edac/amd76x_edac.c34
-rw-r--r--drivers/edac/cell_edac.c28
-rw-r--r--drivers/edac/cpc925_edac.c96
-rw-r--r--drivers/edac/e752x_edac.c92
-rw-r--r--drivers/edac/e7xxx_edac.c89
-rw-r--r--drivers/edac/edac_core.h39
-rw-r--r--drivers/edac/edac_device.c47
-rw-r--r--drivers/edac/edac_device_sysfs.c71
-rw-r--r--drivers/edac/edac_mc.c395
-rw-r--r--drivers/edac/edac_mc_sysfs.c1355
-rw-r--r--drivers/edac/edac_module.c20
-rw-r--r--drivers/edac/edac_module.h26
-rw-r--r--drivers/edac/edac_pci.c26
-rw-r--r--drivers/edac/edac_pci_sysfs.c49
-rw-r--r--drivers/edac/highbank_l2_edac.c149
-rw-r--r--drivers/edac/highbank_mc_edac.c264
-rw-r--r--drivers/edac/i3000_edac.c47
-rw-r--r--drivers/edac/i3200_edac.c48
-rw-r--r--drivers/edac/i5000_edac.c207
-rw-r--r--drivers/edac/i5100_edac.c14
-rw-r--r--drivers/edac/i5400_edac.c201
-rw-r--r--drivers/edac/i7300_edac.c173
-rw-r--r--drivers/edac/i7core_edac.c520
-rw-r--r--drivers/edac/i82443bxgx_edac.c51
-rw-r--r--drivers/edac/i82860_edac.c45
-rw-r--r--drivers/edac/i82875p_edac.c53
-rw-r--r--drivers/edac/i82975x_edac.c55
-rw-r--r--drivers/edac/mpc85xx_edac.c131
-rw-r--r--drivers/edac/mv64x60_edac.c40
-rw-r--r--drivers/edac/pasemi_edac.c22
-rw-r--r--drivers/edac/ppc4xx_edac.c16
-rw-r--r--drivers/edac/r82600_edac.c48
-rw-r--r--drivers/edac/sb_edac.c257
-rw-r--r--drivers/edac/tile_edac.c12
-rw-r--r--drivers/edac/x38_edac.c48
-rw-r--r--drivers/extcon/Kconfig2
-rw-r--r--drivers/extcon/extcon-max8997.c29
-rw-r--r--drivers/firewire/core-device.c9
-rw-r--r--drivers/firewire/core-iso.c2
-rw-r--r--drivers/firewire/core-transaction.c23
-rw-r--r--drivers/firewire/ohci.c30
-rw-r--r--drivers/firmware/dmi_scan.c3
-rw-r--r--drivers/firmware/memmap.c8
-rw-r--r--drivers/firmware/pcdp.c4
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-tps6586x.c158
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c294
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h31
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dmabuf.c33
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c9
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c123
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c40
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c49
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c246
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.h12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c53
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c36
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c48
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hv/vmbus_drv.c3
-rw-r--r--drivers/hwmon/acpi_power_meter.c4
-rw-r--r--drivers/hwmon/applesmc.c70
-rw-r--r--drivers/hwmon/coretemp.c2
-rw-r--r--drivers/hwmon/jc42.c26
-rw-r--r--drivers/hwmon/via-cputemp.c2
-rw-r--r--drivers/i2c/busses/Kconfig7
-rw-r--r--drivers/i2c/busses/i2c-at91.c13
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c147
-rw-r--r--drivers/i2c/busses/i2c-imx.c75
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c133
-rw-r--r--drivers/i2c/busses/i2c-mxs.c68
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c189
-rw-r--r--drivers/i2c/busses/i2c-ocores.c113
-rw-r--r--drivers/i2c/busses/i2c-octeon.c92
-rw-r--r--drivers/i2c/busses/i2c-omap.c155
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c3
-rw-r--r--drivers/i2c/busses/i2c-pnx.c19
-rw-r--r--drivers/i2c/busses/i2c-puv3.c15
-rw-r--r--drivers/i2c/busses/i2c-pxa.c7
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c6
-rw-r--r--drivers/i2c/busses/i2c-stu300.c102
-rw-r--r--drivers/i2c/busses/i2c-tegra.c122
-rw-r--r--drivers/i2c/i2c-core.c44
-rw-r--r--drivers/infiniband/core/cma.c5
-rw-r--r--drivers/infiniband/core/ucma.c19
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c4
-rw-r--r--drivers/infiniband/hw/qib/qib.h10
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h56
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c16
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c646
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c57
-rw-r--r--drivers/input/misc/88pm80x_onkey.c168
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ab8500-ponkey.c4
-rw-r--r--drivers/input/mouse/synaptics.c22
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/input/tablet/wacom_wac.c21
-rw-r--r--drivers/input/tablet/wacom_wac.h3
-rw-r--r--drivers/input/touchscreen/Kconfig13
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c898
-rw-r--r--drivers/md/Kconfig9
-rw-r--r--drivers/md/dm-crypt.c219
-rw-r--r--drivers/md/dm-delay.c2
-rw-r--r--drivers/md/dm-exception-store.c13
-rw-r--r--drivers/md/dm-flakey.c2
-rw-r--r--drivers/md/dm-ioctl.c5
-rw-r--r--drivers/md/dm-linear.c2
-rw-r--r--drivers/md/dm-log.c13
-rw-r--r--drivers/md/dm-mpath.c49
-rw-r--r--drivers/md/dm-raid.c147
-rw-r--r--drivers/md/dm-raid1.c10
-rw-r--r--drivers/md/dm-snap.c34
-rw-r--r--drivers/md/dm-stripe.c87
-rw-r--r--drivers/md/dm-table.c3
-rw-r--r--drivers/md/dm-thin-metadata.c769
-rw-r--r--drivers/md/dm-thin-metadata.h25
-rw-r--r--drivers/md/dm-thin.c542
-rw-r--r--drivers/md/dm-verity.c2
-rw-r--r--drivers/md/dm.c40
-rw-r--r--drivers/md/dm.h5
-rw-r--r--drivers/md/md.c67
-rw-r--r--drivers/md/md.h11
-rw-r--r--drivers/md/persistent-data/Makefile1
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c105
-rw-r--r--drivers/md/persistent-data/dm-block-manager.h21
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.c446
-rw-r--r--drivers/md/persistent-data/dm-space-map-checker.h26
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.c12
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.h1
-rw-r--r--drivers/md/persistent-data/dm-space-map-disk.c34
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.c91
-rw-r--r--drivers/md/persistent-data/dm-transaction-manager.h11
-rw-r--r--drivers/md/raid1.c167
-rw-r--r--drivers/md/raid1.h30
-rw-r--r--drivers/md/raid10.c95
-rw-r--r--drivers/md/raid10.h23
-rw-r--r--drivers/md/raid5.c210
-rw-r--r--drivers/md/raid5.h2
-rw-r--r--drivers/media/Kconfig114
-rw-r--r--drivers/media/common/tuners/Kconfig64
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c249
-rw-r--r--drivers/media/common/tuners/xc5000.c14
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c1
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig3
-rw-r--r--drivers/media/dvb/dvb-usb/az6007.c6
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h3
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c516
-rw-r--r--drivers/media/dvb/frontends/Kconfig8
-rw-r--r--drivers/media/dvb/frontends/Makefile1
-rw-r--r--drivers/media/dvb/frontends/a8293.c37
-rw-r--r--drivers/media/dvb/frontends/dib8000.c4
-rw-r--r--drivers/media/dvb/frontends/drxk.h11
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c350
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.h17
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx.c5
-rw-r--r--drivers/media/dvb/frontends/rtl2832.c789
-rw-r--r--drivers/media/dvb/frontends/rtl2832.h74
-rw-r--r--drivers/media/dvb/frontends/rtl2832_priv.h260
-rw-r--r--drivers/media/dvb/frontends/s5h1420.c20
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c22
-rw-r--r--drivers/media/dvb/frontends/stv0367.c5
-rw-r--r--drivers/media/dvb/frontends/stv090x.c4
-rw-r--r--drivers/media/dvb/frontends/tda10071.c351
-rw-r--r--drivers/media/dvb/frontends/tda10071_priv.h15
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c1
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c39
-rw-r--r--drivers/media/radio/Kconfig34
-rw-r--r--drivers/media/radio/Makefile4
-rw-r--r--drivers/media/radio/lm7000.h43
-rw-r--r--drivers/media/radio/radio-aimslab.c66
-rw-r--r--drivers/media/radio/radio-cadet.c388
-rw-r--r--drivers/media/radio/radio-mr800.c5
-rw-r--r--drivers/media/radio/radio-sf16fmi.c61
-rw-r--r--drivers/media/radio/radio-shark.c376
-rw-r--r--drivers/media/radio/radio-shark2.c348
-rw-r--r--drivers/media/radio/radio-tea5777.c491
-rw-r--r--drivers/media/radio/radio-tea5777.h87
-rw-r--r--drivers/media/radio/radio-wl1273.c3
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c289
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c6
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c47
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h7
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.c2
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c4
-rw-r--r--drivers/media/rc/Kconfig60
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/ati_remote.c133
-rw-r--r--drivers/media/rc/ene_ir.c3
-rw-r--r--drivers/media/rc/fintek-cir.c32
-rw-r--r--drivers/media/rc/gpio-ir-recv.c26
-rw-r--r--drivers/media/rc/iguanair.c639
-rw-r--r--drivers/media/rc/mceusb.c20
-rw-r--r--drivers/media/rc/nuvoton-cir.c145
-rw-r--r--drivers/media/rc/rc-main.c5
-rw-r--r--drivers/media/video/Kconfig85
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/adv7180.c235
-rw-r--r--drivers/media/video/adv7393.c487
-rw-r--r--drivers/media/video/adv7393_regs.h188
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c11
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c6
-rw-r--r--drivers/media/video/bt8xx/bttv.h2
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c2
-rw-r--r--drivers/media/video/cs8420.h50
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c18
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h2
-rw-r--r--drivers/media/video/cx18/cx18-streams.c4
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c56
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c17
-rw-r--r--drivers/media/video/cx231xx/cx231xx-i2c.c8
-rw-r--r--drivers/media/video/cx231xx/cx231xx.h2
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c10
-rw-r--r--drivers/media/video/cx23885/cx23885.h2
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c10
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c2
-rw-r--r--drivers/media/video/cx25821/cx25821.h2
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c31
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c234
-rw-r--r--drivers/media/video/cx88/cx88-cards.c20
-rw-r--r--drivers/media/video/cx88/cx88-core.c7
-rw-r--r--drivers/media/video/cx88/cx88-video.c901
-rw-r--r--drivers/media/video/cx88/cx88.h68
-rw-r--r--drivers/media/video/davinci/Kconfig30
-rw-r--r--drivers/media/video/davinci/Makefile8
-rw-r--r--drivers/media/video/davinci/vpbe_display.c4
-rw-r--r--drivers/media/video/davinci/vpif.c45
-rw-r--r--drivers/media/video/davinci/vpif.h45
-rw-r--r--drivers/media/video/davinci/vpif_capture.c694
-rw-r--r--drivers/media/video/davinci/vpif_capture.h16
-rw-r--r--drivers/media/video/davinci/vpif_display.c684
-rw-r--r--drivers/media/video/davinci/vpif_display.h23
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c27
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c7
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c33
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c95
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c1
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h51
-rw-r--r--drivers/media/video/gspca/benq.c7
-rw-r--r--drivers/media/video/gspca/conex.c208
-rw-r--r--drivers/media/video/gspca/cpia1.c486
-rw-r--r--drivers/media/video/gspca/etoms.c221
-rw-r--r--drivers/media/video/gspca/finepix.c1
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c1
-rw-r--r--drivers/media/video/gspca/gspca.c50
-rw-r--r--drivers/media/video/gspca/jeilinj.c219
-rw-r--r--drivers/media/video/gspca/jl2005bcd.c3
-rw-r--r--drivers/media/video/gspca/kinect.c10
-rw-r--r--drivers/media/video/gspca/konica.c289
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c1
-rw-r--r--drivers/media/video/gspca/mars.c48
-rw-r--r--drivers/media/video/gspca/mr97310a.c439
-rw-r--r--drivers/media/video/gspca/nw80x.c203
-rw-r--r--drivers/media/video/gspca/ov519.c600
-rw-r--r--drivers/media/video/gspca/ov534.c570
-rw-r--r--drivers/media/video/gspca/ov534_9.c294
-rw-r--r--drivers/media/video/gspca/pac207.c1
-rw-r--r--drivers/media/video/gspca/pac7302.c372
-rw-r--r--drivers/media/video/gspca/pac7311.c1
-rw-r--r--drivers/media/video/gspca/se401.c184
-rw-r--r--drivers/media/video/gspca/sn9c2028.c7
-rw-r--r--drivers/media/video/gspca/sonixb.c622
-rw-r--r--drivers/media/video/gspca/sonixj.c1
-rw-r--r--drivers/media/video/gspca/spca1528.c271
-rw-r--r--drivers/media/video/gspca/spca500.c201
-rw-r--r--drivers/media/video/gspca/spca501.c257
-rw-r--r--drivers/media/video/gspca/spca505.c77
-rw-r--r--drivers/media/video/gspca/spca506.c209
-rw-r--r--drivers/media/video/gspca/spca508.c71
-rw-r--r--drivers/media/video/gspca/spca561.c393
-rw-r--r--drivers/media/video/gspca/sq905.c1
-rw-r--r--drivers/media/video/gspca/sq905c.c1
-rw-r--r--drivers/media/video/gspca/sq930x.c110
-rw-r--r--drivers/media/video/gspca/stk014.c188
-rw-r--r--drivers/media/video/gspca/stv0680.c7
-rw-r--r--drivers/media/video/gspca/sunplus.c237
-rw-r--r--drivers/media/video/gspca/t613.c824
-rw-r--r--drivers/media/video/gspca/topro.c459
-rw-r--r--drivers/media/video/gspca/tv8532.c125
-rw-r--r--drivers/media/video/gspca/vc032x.c694
-rw-r--r--drivers/media/video/gspca/vicam.c68
-rw-r--r--drivers/media/video/gspca/w996Xcf.c15
-rw-r--r--drivers/media/video/gspca/xirlink_cit.c473
-rw-r--r--drivers/media/video/ibmmpeg2.h94
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c12
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.h1
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c4
-rw-r--r--drivers/media/video/m5mols/Kconfig1
-rw-r--r--drivers/media/video/m5mols/m5mols_controls.c4
-rw-r--r--drivers/media/video/mem2mem_testdev.c332
-rw-r--r--drivers/media/video/mt9m001.c2
-rw-r--r--drivers/media/video/mt9m032.c13
-rw-r--r--drivers/media/video/mt9m111.c1
-rw-r--r--drivers/media/video/mt9p031.c5
-rw-r--r--drivers/media/video/mt9t001.c13
-rw-r--r--drivers/media/video/mt9v022.c2
-rw-r--r--drivers/media/video/mx2_emmaprp.c10
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c8
-rw-r--r--drivers/media/video/omap3isp/isppreview.c8
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c6
-rw-r--r--drivers/media/video/ov2640.c6
-rw-r--r--drivers/media/video/ov772x.c8
-rw-r--r--drivers/media/video/ov9640.c1
-rw-r--r--drivers/media/video/pms.c1
-rw-r--r--drivers/media/video/pvrusb2/Kconfig1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c12
-rw-r--r--drivers/media/video/pwc/pwc-if.c171
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c165
-rw-r--r--drivers/media/video/pwc/pwc.h3
-rw-r--r--drivers/media/video/s2255drv.c1
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c112
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h3
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.c2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.c15
-rw-r--r--drivers/media/video/s5p-fimc/fimc-m2m.c52
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c7
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c20
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c9
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c38
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c10
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c11
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c8
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c12
-rw-r--r--drivers/media/video/saa7121.h132
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c82
-rw-r--r--drivers/media/video/saa7146.h112
-rw-r--r--drivers/media/video/saa7146reg.h283
-rw-r--r--drivers/media/video/saa7164/saa7164-api.c14
-rw-r--r--drivers/media/video/saa7164/saa7164-i2c.c20
-rw-r--r--drivers/media/video/saa7164/saa7164.h2
-rw-r--r--drivers/media/video/smiapp/Kconfig1
-rw-r--r--drivers/media/video/smiapp/smiapp-core.c41
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h2
-rw-r--r--drivers/media/video/soc_camera.c9
-rw-r--r--drivers/media/video/tlg2300/pd-main.c4
-rw-r--r--drivers/media/video/tuner-core.c15
-rw-r--r--drivers/media/video/tvaudio.c291
-rw-r--r--drivers/media/video/tvp5150.c97
-rw-r--r--drivers/media/video/tw9910.c8
-rw-r--r--drivers/media/video/uvc/Kconfig1
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c5
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c2
-rw-r--r--drivers/media/video/uvc/uvc_video.c8
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c10
-rw-r--r--drivers/media/video/v4l2-ctrls.c3
-rw-r--r--drivers/media/video/v4l2-dev.c70
-rw-r--r--drivers/media/video/v4l2-ioctl.c3359
-rw-r--r--drivers/media/video/v4l2-mem2mem.c18
-rw-r--r--drivers/media/video/v4l2-subdev.c4
-rw-r--r--drivers/media/video/via-camera.c2
-rw-r--r--drivers/media/video/videobuf-core.c16
-rw-r--r--drivers/media/video/videobuf-dma-contig.c57
-rw-r--r--drivers/media/video/videobuf2-core.c417
-rw-r--r--drivers/media/video/vivi.c220
-rw-r--r--drivers/media/video/zr364xx.c484
-rw-r--r--drivers/message/i2o/i2o_config.c7
-rw-r--r--drivers/message/i2o/i2o_proc.c37
-rw-r--r--drivers/mfd/88pm800.c596
-rw-r--r--drivers/mfd/88pm805.c301
-rw-r--r--drivers/mfd/88pm80x.c145
-rw-r--r--drivers/mfd/88pm860x-core.c23
-rw-r--r--drivers/mfd/Kconfig99
-rw-r--r--drivers/mfd/Makefile15
-rw-r--r--drivers/mfd/ab3100-core.c23
-rw-r--r--drivers/mfd/ab8500-core.c242
-rw-r--r--drivers/mfd/ab8500-debugfs.c12
-rw-r--r--drivers/mfd/ab8500-gpadc.c9
-rw-r--r--drivers/mfd/ab8500-sysctrl.c6
-rw-r--r--drivers/mfd/adp5520.c2
-rw-r--r--drivers/mfd/anatop-mfd.c2
-rw-r--r--drivers/mfd/arizona-core.c566
-rw-r--r--drivers/mfd/arizona-i2c.c97
-rw-r--r--drivers/mfd/arizona-irq.c275
-rw-r--r--drivers/mfd/arizona-spi.c97
-rw-r--r--drivers/mfd/arizona.h40
-rw-r--r--drivers/mfd/da9052-core.c1
-rw-r--r--drivers/mfd/db8500-prcmu.c92
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h1
-rw-r--r--drivers/mfd/max77686-irq.c319
-rw-r--r--drivers/mfd/max77686.c187
-rw-r--r--drivers/mfd/max77693.c11
-rw-r--r--drivers/mfd/max8925-core.c8
-rw-r--r--drivers/mfd/max8997-irq.c62
-rw-r--r--drivers/mfd/max8997.c9
-rw-r--r--drivers/mfd/mc13xxx-core.c4
-rw-r--r--drivers/mfd/mc13xxx-i2c.c12
-rw-r--r--drivers/mfd/mc13xxx-spi.c15
-rw-r--r--drivers/mfd/mfd-core.c30
-rw-r--r--drivers/mfd/pcf50633-core.c9
-rw-r--r--drivers/mfd/s5m-core.c206
-rw-r--r--drivers/mfd/s5m-irq.c495
-rw-r--r--drivers/mfd/sec-core.c216
-rw-r--r--drivers/mfd/sec-irq.c317
-rw-r--r--drivers/mfd/tc3589x.c9
-rw-r--r--drivers/mfd/timberdale.c2
-rw-r--r--drivers/mfd/tps65010.c3
-rw-r--r--drivers/mfd/tps65090.c4
-rw-r--r--drivers/mfd/tps6586x.c296
-rw-r--r--drivers/mfd/tps65910.c23
-rw-r--r--drivers/mfd/twl-core.c12
-rw-r--r--drivers/mfd/twl6040-core.c24
-rw-r--r--drivers/mfd/wm5102-tables.c2399
-rw-r--r--drivers/mfd/wm5110-tables.c2281
-rw-r--r--drivers/mfd/wm831x-otp.c8
-rw-r--r--drivers/mfd/wm8350-core.c354
-rw-r--r--drivers/mfd/wm8350-i2c.c5
-rw-r--r--drivers/mfd/wm8350-irq.c8
-rw-r--r--drivers/mfd/wm8350-regmap.c3222
-rw-r--r--drivers/mfd/wm8994-core.c17
-rw-r--r--drivers/mfd/wm8994-irq.c10
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/ab8500-pwm.c6
-rw-r--r--drivers/misc/lkdtm.c2
-rw-r--r--drivers/misc/ti-st/st_core.c5
-rw-r--r--drivers/mmc/host/omap.c368
-rw-r--r--drivers/mmc/host/omap_hsmmc.c204
-rw-r--r--drivers/mtd/nand/jz4740_nand.c228
-rw-r--r--drivers/mtd/nand/omap2.c106
-rw-r--r--drivers/net/can/c_can/c_can_platform.c8
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c1
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c312
-rw-r--r--drivers/net/ethernet/sfc/efx.c6
-rw-r--r--drivers/net/ethernet/sfc/efx.h14
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c16
-rw-r--r--drivers/net/ethernet/sfc/tx.c19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h42
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c33
-rw-r--r--drivers/net/phy/mdio-octeon.c92
-rw-r--r--drivers/net/usb/cdc-phonet.c2
-rw-r--r--drivers/net/wireless/at76c50x-usb.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.h1
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c105
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c3
-rw-r--r--drivers/net/wireless/b43/main.c21
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c30
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c15
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c5
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c6
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/debugfs.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c13
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c30
-rw-r--r--drivers/net/wireless/libertas/cfg.c1
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c6
-rw-r--r--drivers/net/wireless/libertas/main.c5
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c15
-rw-r--r--drivers/net/wireless/rndis_wlan.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c11
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.h17
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c69
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c80
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c22
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c12
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.h3
-rw-r--r--drivers/platform/x86/acer-wmi.c153
-rw-r--r--drivers/platform/x86/apple-gmux.c6
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c108
-rw-r--r--drivers/platform/x86/asus-wmi.c23
-rw-r--r--drivers/platform/x86/asus-wmi.h1
-rw-r--r--drivers/platform/x86/classmate-laptop.c405
-rw-r--r--drivers/platform/x86/dell-laptop.c54
-rw-r--r--drivers/platform/x86/eeepc-wmi.c25
-rw-r--r--drivers/platform/x86/samsung-laptop.c41
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c5
-rw-r--r--drivers/power/Kconfig1
-rw-r--r--drivers/power/bq27x00_battery.c155
-rw-r--r--drivers/power/charger-manager.c152
-rw-r--r--drivers/power/ds2781_battery.c2
-rw-r--r--drivers/power/gpio-charger.c2
-rw-r--r--drivers/power/lp8727_charger.c2
-rw-r--r--drivers/power/max17042_battery.c8
-rw-r--r--drivers/power/olpc_battery.c62
-rw-r--r--drivers/power/pda_power.c10
-rw-r--r--drivers/power/power_supply_core.c65
-rw-r--r--drivers/power/power_supply_sysfs.c8
-rw-r--r--drivers/power/sbs-battery.c2
-rw-r--r--drivers/power/smb347-charger.c123
-rw-r--r--drivers/power/test_power.c75
-rw-r--r--drivers/power/twl4030_charger.c80
-rw-r--r--drivers/pps/pps.c4
-rw-r--r--drivers/pwm/Kconfig108
-rw-r--r--drivers/pwm/Makefile11
-rw-r--r--drivers/pwm/core.c713
-rw-r--r--drivers/pwm/pwm-bfin.c162
-rw-r--r--drivers/pwm/pwm-imx.c230
-rw-r--r--drivers/pwm/pwm-lpc32xx.c148
-rw-r--r--drivers/pwm/pwm-mxs.c203
-rw-r--r--drivers/pwm/pwm-pxa.c218
-rw-r--r--drivers/pwm/pwm-samsung.c356
-rw-r--r--drivers/pwm/pwm-tegra.c261
-rw-r--r--drivers/pwm/pwm-tiecap.c232
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c411
-rw-r--r--drivers/pwm/pwm-vt8500.c177
-rw-r--r--drivers/regulator/Kconfig2
-rw-r--r--drivers/regulator/ab8500.c6
-rw-r--r--drivers/regulator/db8500-prcmu.c6
-rw-r--r--drivers/regulator/s5m8767.c78
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-88pm80x.c369
-rw-r--r--drivers/rtc/rtc-ab8500.c42
-rw-r--r--drivers/rtc/rtc-coh901331.c61
-rw-r--r--drivers/rtc/rtc-da9052.c5
-rw-r--r--drivers/rtc/rtc-max8925.c13
-rw-r--r--drivers/rtc/rtc-mc13xxx.c6
-rw-r--r--drivers/rtc/rtc-pcf8563.c11
-rw-r--r--drivers/rtc/rtc-pl031.c95
-rw-r--r--drivers/rtc/rtc-r9701.c6
-rw-r--r--drivers/rtc/rtc-s3c.c4
-rw-r--r--drivers/rtc/rtc-wm831x.c24
-rw-r--r--drivers/scsi/scsi_transport_fc.c38
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c2
-rw-r--r--drivers/spi/Kconfig9
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-falcon.c469
-rw-r--r--drivers/spi/spi-omap2-mcspi.c229
-rw-r--r--drivers/staging/bcm/Misc.c31
-rw-r--r--drivers/staging/gdm72xx/sdio_boot.c7
-rw-r--r--drivers/staging/gdm72xx/usb_boot.c22
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c15
-rw-r--r--drivers/staging/media/easycap/easycap_main.c1
-rw-r--r--drivers/staging/media/lirc/lirc_bt829.c2
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c60
-rw-r--r--drivers/staging/media/solo6x10/TODO2
-rw-r--r--drivers/staging/media/solo6x10/core.c13
-rw-r--r--drivers/staging/media/solo6x10/i2c.c2
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c28
-rw-r--r--drivers/staging/octeon/ethernet.c153
-rw-r--r--drivers/staging/octeon/octeon-ethernet.h3
-rw-r--r--drivers/target/target_core_file.c32
-rw-r--r--drivers/thermal/thermal_sys.c2
-rw-r--r--drivers/tty/serial/uartlite.c3
-rw-r--r--drivers/usb/core/hub.c9
-rw-r--r--drivers/usb/gadget/f_phonet.c2
-rw-r--r--drivers/usb/gadget/goku_udc.c2
-rw-r--r--drivers/usb/gadget/m66592-udc.c9
-rw-r--r--drivers/usb/gadget/m66592-udc.h5
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c12
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c11
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h5
-rw-r--r--drivers/usb/gadget/storage_common.c12
-rw-r--r--drivers/usb/gadget/u_uac1.c6
-rw-r--r--drivers/usb/host/ehci-omap.c8
-rw-r--r--drivers/usb/host/r8a66597-hcd.c12
-rw-r--r--drivers/usb/host/r8a66597.h5
-rw-r--r--drivers/usb/musb/musb_core.h8
-rw-r--r--drivers/usb/otg/isp1301_omap.c1
-rw-r--r--drivers/vfio/Kconfig16
-rw-r--r--drivers/vfio/Makefile3
-rw-r--r--drivers/vfio/pci/Kconfig8
-rw-r--r--drivers/vfio/pci/Makefile4
-rw-r--r--drivers/vfio/pci/vfio_pci.c579
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c1540
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c740
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h91
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c269
-rw-r--r--drivers/vfio/vfio.c1420
-rw-r--r--drivers/vfio/vfio_iommu_type1.c753
-rw-r--r--drivers/video/aty/aty128fb.c180
-rw-r--r--drivers/video/backlight/Kconfig2
-rw-r--r--drivers/video/backlight/atmel-pwm-bl.c19
-rw-r--r--drivers/video/backlight/corgi_lcd.c19
-rw-r--r--drivers/video/backlight/l4f00242t03.c29
-rw-r--r--drivers/video/backlight/lm3533_bl.c8
-rw-r--r--drivers/video/backlight/lms283gf05.c24
-rw-r--r--drivers/video/backlight/lp855x_bl.c10
-rw-r--r--drivers/video/backlight/ot200_bl.c21
-rw-r--r--drivers/video/backlight/pwm_bl.c159
-rw-r--r--drivers/video/backlight/tosa_bl.c8
-rw-r--r--drivers/video/backlight/tosa_lcd.c7
-rw-r--r--drivers/video/da8xx-fb.c78
-rw-r--r--drivers/video/epson1355fb.c8
-rw-r--r--drivers/video/exynos/exynos_dp_core.c23
-rw-r--r--drivers/video/exynos/exynos_dp_core.h4
-rw-r--r--drivers/video/exynos/exynos_dp_reg.c4
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c2
-rw-r--r--drivers/video/exynos/s6e8ax0.h21
-rw-r--r--drivers/video/fb_defio.c2
-rw-r--r--drivers/video/fb_draw.h7
-rw-r--r--drivers/video/grvga.c47
-rw-r--r--drivers/video/mx3fb.c55
-rw-r--r--drivers/video/omap2/displays/panel-acx565akm.c10
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c179
-rw-r--r--drivers/video/omap2/displays/panel-lgphilips-lb035q02.c8
-rw-r--r--drivers/video/omap2/displays/panel-n8x0.c1
-rw-r--r--drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c9
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.c9
-rw-r--r--drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c9
-rw-r--r--drivers/video/omap2/displays/panel-taal.c1
-rw-r--r--drivers/video/omap2/displays/panel-tfp410.c7
-rw-r--r--drivers/video/omap2/displays/panel-tpo-td043mtea1.c8
-rw-r--r--drivers/video/omap2/dss/Kconfig4
-rw-r--r--drivers/video/omap2/dss/apply.c91
-rw-r--r--drivers/video/omap2/dss/dispc.c494
-rw-r--r--drivers/video/omap2/dss/dispc.h28
-rw-r--r--drivers/video/omap2/dss/display.c40
-rw-r--r--drivers/video/omap2/dss/dpi.c64
-rw-r--r--drivers/video/omap2/dss/dsi.c152
-rw-r--r--drivers/video/omap2/dss/dss.c19
-rw-r--r--drivers/video/omap2/dss/dss.h54
-rw-r--r--drivers/video/omap2/dss/dss_features.h5
-rw-r--r--drivers/video/omap2/dss/hdmi.c246
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c9
-rw-r--r--drivers/video/omap2/dss/manager.c51
-rw-r--r--drivers/video/omap2/dss/overlay.c33
-rw-r--r--drivers/video/omap2/dss/rfbi.c40
-rw-r--r--drivers/video/omap2/dss/sdi.c42
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h21
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c26
-rw-r--r--drivers/video/omap2/dss/venc.c8
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c51
-rw-r--r--drivers/video/s3fb.c31
-rw-r--r--drivers/video/sh_mipi_dsi.c7
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1117
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h5
-rw-r--r--drivers/video/sh_mobile_meram.c235
-rw-r--r--drivers/video/smscufx.c2
-rw-r--r--drivers/video/w100fb.c12
706 files changed, 48908 insertions, 27246 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index bfc918633fd9..ece958d3762e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
+source "drivers/vfio/Kconfig"
+
source "drivers/vlynq/Kconfig"
source "drivers/virtio/Kconfig"
@@ -148,4 +150,6 @@ source "drivers/iio/Kconfig"
source "drivers/vme/Kconfig"
+source "drivers/pwm/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 2ba29ffef2cb..5b421840c48d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,7 @@
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
obj-y += gpio/
+obj-y += pwm/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
obj-$(CONFIG_RAPIDIO) += rapidio/
@@ -59,6 +60,7 @@ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
obj-$(CONFIG_UIO) += uio/
+obj-$(CONFIG_VFIO) += vfio/
obj-y += cdrom/
obj-y += auxdisplay/
obj-$(CONFIG_PCCARD) += pcmcia/
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 45d8097ef4cf..b728880ef10e 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -132,6 +132,33 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
+/* Force to use vendor driver when the ACPI device is known to be
+ * buggy */
+static int video_detect_force_vendor(const struct dmi_system_id *d)
+{
+ acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+ return 0;
+}
+
+static struct dmi_system_id video_detect_dmi_table[] = {
+ /* On Samsung X360, the BIOS will set a flag (VDRV) if generic
+ * ACPI backlight device is used. This flag will definitively break
+ * the backlight interface (even the vendor interface) untill next
+ * reboot. It's why we should prevent video.ko from being used here
+ * and we can't rely on a later call to acpi_video_unregister().
+ */
+ {
+ .callback = video_detect_force_vendor,
+ .ident = "X360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
+ DMI_MATCH(DMI_BOARD_NAME, "X360"),
+ },
+ },
+ { },
+};
+
/*
* Returns the video capabilities of a specific ACPI graphics device
*
@@ -164,6 +191,8 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
* ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
*}
*/
+
+ dmi_check_system(video_detect_dmi_table);
} else {
status = acpi_bus_get_device(graphics_handle, &tmp_dev);
if (ACPI_FAILURE(status)) {
@@ -182,8 +211,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
}
EXPORT_SYMBOL(acpi_video_get_capabilities);
-/* Returns true if video.ko can do backlight switching */
-int acpi_video_backlight_support(void)
+static void acpi_video_caps_check(void)
{
/*
* We must check whether the ACPI graphics device is physically plugged
@@ -191,6 +219,34 @@ int acpi_video_backlight_support(void)
*/
if (!acpi_video_caps_checked)
acpi_video_get_capabilities(NULL);
+}
+
+/* Promote the vendor interface instead of the generic video module.
+ * This function allow DMI blacklists to be implemented by externals
+ * platform drivers instead of putting a big blacklist in video_detect.c
+ * After calling this function you will probably want to call
+ * acpi_video_unregister() to make sure the video module is not loaded
+ */
+void acpi_video_dmi_promote_vendor(void)
+{
+ acpi_video_caps_check();
+ acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+}
+EXPORT_SYMBOL(acpi_video_dmi_promote_vendor);
+
+/* To be called when a driver who previously promoted the vendor
+ * interface */
+void acpi_video_dmi_demote_vendor(void)
+{
+ acpi_video_caps_check();
+ acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+}
+EXPORT_SYMBOL(acpi_video_dmi_demote_vendor);
+
+/* Returns true if video.ko can do backlight switching */
+int acpi_video_backlight_support(void)
+{
+ acpi_video_caps_check();
/* First check for boot param -> highest prio */
if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index ac6a5beb28f3..bfaa5cb1629a 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -184,10 +184,8 @@
struct arasan_cf_dev {
/* pointer to ata_host structure */
struct ata_host *host;
- /* clk structure, only if HAVE_CLK is defined */
-#ifdef CONFIG_HAVE_CLK
+ /* clk structure */
struct clk *clk;
-#endif
/* physical base address of controller */
dma_addr_t pbase;
@@ -312,13 +310,11 @@ static int cf_init(struct arasan_cf_dev *acdev)
unsigned long flags;
int ret = 0;
-#ifdef CONFIG_HAVE_CLK
ret = clk_enable(acdev->clk);
if (ret) {
dev_dbg(acdev->host->dev, "clock enable failed");
return ret;
}
-#endif
spin_lock_irqsave(&acdev->host->lock, flags);
/* configure CF interface clock */
@@ -344,9 +340,7 @@ static void cf_exit(struct arasan_cf_dev *acdev)
writel(readl(acdev->vbase + OP_MODE) & ~CFHOST_ENB,
acdev->vbase + OP_MODE);
spin_unlock_irqrestore(&acdev->host->lock, flags);
-#ifdef CONFIG_HAVE_CLK
clk_disable(acdev->clk);
-#endif
}
static void dma_callback(void *dev)
@@ -828,13 +822,11 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev)
return -ENOMEM;
}
-#ifdef CONFIG_HAVE_CLK
acdev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(acdev->clk)) {
dev_warn(&pdev->dev, "Clock not found\n");
return PTR_ERR(acdev->clk);
}
-#endif
/* allocate host */
host = ata_host_alloc(&pdev->dev, 1);
@@ -899,9 +891,7 @@ static int __devinit arasan_cf_probe(struct platform_device *pdev)
&arasan_cf_sht);
free_clk:
-#ifdef CONFIG_HAVE_CLK
clk_put(acdev->clk);
-#endif
return ret;
}
@@ -912,9 +902,7 @@ static int __devexit arasan_cf_remove(struct platform_device *pdev)
ata_host_detach(host);
cf_exit(acdev);
-#ifdef CONFIG_HAVE_CLK
clk_put(acdev->clk);
-#endif
return 0;
}
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 9b21469482ae..08b4c5209384 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -196,6 +196,7 @@ config CMA
bool "Contiguous Memory Allocator (EXPERIMENTAL)"
depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL
select MIGRATION
+ select MEMORY_ISOLATION
help
This enables the Contiguous Memory Allocator which allows drivers
to allocate big physically-contiguous blocks of memory for use with
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index d91a3a0b2325..deb4a456cf83 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -156,9 +156,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (!err)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
@@ -218,10 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
}
- dput(dentry);
-
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 6f3676f1559f..3fbedc75e7c5 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -10,6 +10,7 @@
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/gfp.h>
+#include <asm-generic/dma-coherent.h>
/*
* Managed DMA API
@@ -217,4 +218,52 @@ void dmam_release_declared_memory(struct device *dev)
}
EXPORT_SYMBOL(dmam_release_declared_memory);
+/*
+ * Create scatter-list for the already allocated DMA buffer.
+ */
+int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+ void *cpu_addr, dma_addr_t handle, size_t size)
+{
+ struct page *page = virt_to_page(cpu_addr);
+ int ret;
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (unlikely(ret))
+ return ret;
+
+ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+ return 0;
+}
+EXPORT_SYMBOL(dma_common_get_sgtable);
+
#endif
+
+/*
+ * Create userspace mapping for the DMA-coherent memory.
+ */
+int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size)
+{
+ int ret = -ENXIO;
+#ifdef CONFIG_MMU
+ unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
+ unsigned long off = vma->vm_pgoff;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
+ return ret;
+
+ if (off < count && user_count <= (count - off)) {
+ ret = remap_pfn_range(vma, vma->vm_start,
+ pfn + off,
+ user_count << PAGE_SHIFT,
+ vma->vm_page_prot);
+ }
+#endif /* CONFIG_MMU */
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_common_mmap);
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index fc996288090b..f7b0af7100cd 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -273,6 +273,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
{ 0, },
};
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 26823d97fd9f..9ea4627dc0c2 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
/* for these chips OTP is always available */
present = true;
break;
-
+ case BCMA_CHIP_ID_BCM43228:
+ present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
+ break;
default:
present = false;
break;
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index e54e31b02b88..3fbef018ce55 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -411,7 +411,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+ mdev->ldev->md.al_offset + mdev->al_tr_pos;
if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE))
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
if (++mdev->al_tr_pos >
div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT))
@@ -876,7 +876,11 @@ int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size,
unsigned int enr, count = 0;
struct lc_element *e;
- if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
+ /* this should be an empty REQ_FLUSH */
+ if (size == 0)
+ return 0;
+
+ if (size < 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
dev_err(DEV, "sector: %llus, size: %d\n",
(unsigned long long)sector, size);
return 0;
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index fcb956bb4b4c..ba91b408abad 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -1096,7 +1096,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_w
if (ctx->error) {
dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
err = -EIO; /* ctx->error ? */
}
@@ -1212,7 +1212,7 @@ int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(loc
wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
if (ctx->error)
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
/* that should force detach, so the in memory bitmap will be
* gone in a moment as well. */
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 02f013a073a7..b2ca143d0053 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -813,7 +813,6 @@ enum {
SIGNAL_ASENDER, /* whether asender wants to be interrupted */
SEND_PING, /* whether asender should send a ping asap */
- UNPLUG_QUEUED, /* only relevant with kernel 2.4 */
UNPLUG_REMOTE, /* sending a "UnplugRemote" could help */
MD_DIRTY, /* current uuids and flags not yet on disk */
DISCARD_CONCURRENT, /* Set on one node, cleared on the peer! */
@@ -824,7 +823,6 @@ enum {
CRASHED_PRIMARY, /* This node was a crashed primary.
* Gets cleared when the state.conn
* goes into C_CONNECTED state. */
- NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */
CONSIDER_RESYNC,
MD_NO_FUA, /* Users wants us to not use FUA/FLUSH on meta data dev */
@@ -834,6 +832,7 @@ enum {
BITMAP_IO_QUEUED, /* Started bitmap IO */
GO_DISKLESS, /* Disk is being detached, on io-error or admin request. */
WAS_IO_ERROR, /* Local disk failed returned IO error */
+ FORCE_DETACH, /* Force-detach from local disk, aborting any pending local IO */
RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */
NET_CONGESTED, /* The data socket is congested */
@@ -851,6 +850,13 @@ enum {
AL_SUSPENDED, /* Activity logging is currently suspended. */
AHEAD_TO_SYNC_SOURCE, /* Ahead -> SyncSource queued */
STATE_SENT, /* Do not change state/UUIDs while this is set */
+
+ CALLBACK_PENDING, /* Whether we have a call_usermodehelper(, UMH_WAIT_PROC)
+ * pending, from drbd worker context.
+ * If set, bdi_write_congested() returns true,
+ * so shrink_page_list() would not recurse into,
+ * and potentially deadlock on, this drbd worker.
+ */
};
struct drbd_bitmap; /* opaque for drbd_conf */
@@ -1130,8 +1136,8 @@ struct drbd_conf {
int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
int rs_planed; /* resync sectors already planned */
atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
- int peer_max_bio_size;
- int local_max_bio_size;
+ unsigned int peer_max_bio_size;
+ unsigned int local_max_bio_size;
};
static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
@@ -1435,9 +1441,9 @@ struct bm_extent {
* hash table. */
#define HT_SHIFT 8
#define DRBD_MAX_BIO_SIZE (1U<<(9+HT_SHIFT))
-#define DRBD_MAX_BIO_SIZE_SAFE (1 << 12) /* Works always = 4k */
+#define DRBD_MAX_BIO_SIZE_SAFE (1U << 12) /* Works always = 4k */
-#define DRBD_MAX_SIZE_H80_PACKET (1 << 15) /* The old header only allows packets up to 32Kib data */
+#define DRBD_MAX_SIZE_H80_PACKET (1U << 15) /* The old header only allows packets up to 32Kib data */
/* Number of elements in the app_reads_hash */
#define APP_R_HSIZE 15
@@ -1840,12 +1846,20 @@ static inline int drbd_request_state(struct drbd_conf *mdev,
return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED);
}
+enum drbd_force_detach_flags {
+ DRBD_IO_ERROR,
+ DRBD_META_IO_ERROR,
+ DRBD_FORCE_DETACH,
+};
+
#define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__)
-static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where)
+static inline void __drbd_chk_io_error_(struct drbd_conf *mdev,
+ enum drbd_force_detach_flags forcedetach,
+ const char *where)
{
switch (mdev->ldev->dc.on_io_error) {
case EP_PASS_ON:
- if (!forcedetach) {
+ if (forcedetach == DRBD_IO_ERROR) {
if (__ratelimit(&drbd_ratelimit_state))
dev_err(DEV, "Local IO failed in %s.\n", where);
if (mdev->state.disk > D_INCONSISTENT)
@@ -1856,6 +1870,8 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
case EP_DETACH:
case EP_CALL_HELPER:
set_bit(WAS_IO_ERROR, &mdev->flags);
+ if (forcedetach == DRBD_FORCE_DETACH)
+ set_bit(FORCE_DETACH, &mdev->flags);
if (mdev->state.disk > D_FAILED) {
_drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL);
dev_err(DEV,
@@ -1875,7 +1891,7 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
*/
#define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__)
static inline void drbd_chk_io_error_(struct drbd_conf *mdev,
- int error, int forcedetach, const char *where)
+ int error, enum drbd_force_detach_flags forcedetach, const char *where)
{
if (error) {
unsigned long flags;
@@ -2405,15 +2421,17 @@ static inline void dec_ap_bio(struct drbd_conf *mdev)
int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt);
D_ASSERT(ap_bio >= 0);
+
+ if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
+ if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
+ drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
+ }
+
/* this currently does wake_up for every dec_ap_bio!
* maybe rather introduce some type of hysteresis?
* e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */
if (ap_bio < mxb)
wake_up(&mdev->misc_wait);
- if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
- if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
- drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
- }
}
static inline int drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val)
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 920ede2829d6..2e0e7fc1dbba 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -1514,6 +1514,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
/* Do not change the order of the if above and the two below... */
if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */
+ /* we probably will start a resync soon.
+ * make sure those things are properly reset. */
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+ drbd_rs_cancel_all(mdev);
+
drbd_send_uuids(mdev);
drbd_send_state(mdev, ns);
}
@@ -1630,9 +1637,24 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
eh = mdev->ldev->dc.on_io_error;
was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
- /* Immediately allow completion of all application IO, that waits
- for completion from the local disk. */
- tl_abort_disk_io(mdev);
+ if (was_io_error && eh == EP_CALL_HELPER)
+ drbd_khelper(mdev, "local-io-error");
+
+ /* Immediately allow completion of all application IO,
+ * that waits for completion from the local disk,
+ * if this was a force-detach due to disk_timeout
+ * or administrator request (drbdsetup detach --force).
+ * Do NOT abort otherwise.
+ * Aborting local requests may cause serious problems,
+ * if requests are completed to upper layers already,
+ * and then later the already submitted local bio completes.
+ * This can cause DMA into former bio pages that meanwhile
+ * have been re-used for other things.
+ * So aborting local requests may cause crashes,
+ * or even worse, silent data corruption.
+ */
+ if (test_and_clear_bit(FORCE_DETACH, &mdev->flags))
+ tl_abort_disk_io(mdev);
/* current state still has to be D_FAILED,
* there is only one way out: to D_DISKLESS,
@@ -1653,9 +1675,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
drbd_md_sync(mdev);
}
put_ldev(mdev);
-
- if (was_io_error && eh == EP_CALL_HELPER)
- drbd_khelper(mdev, "local-io-error");
}
/* second half of local IO error, failure to attach,
@@ -1669,10 +1688,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
"ASSERT FAILED: disk is %s while going diskless\n",
drbd_disk_str(mdev->state.disk));
- mdev->rs_total = 0;
- mdev->rs_failed = 0;
- atomic_set(&mdev->rs_pending_cnt, 0);
-
if (ns.conn >= C_CONNECTED)
drbd_send_state(mdev, ns);
@@ -2194,7 +2209,8 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
{
struct p_sizes p;
sector_t d_size, u_size;
- int q_order_type, max_bio_size;
+ int q_order_type;
+ unsigned int max_bio_size;
int ok;
if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
@@ -2203,7 +2219,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
u_size = mdev->ldev->dc.disk_size;
q_order_type = drbd_queue_order_type(mdev);
max_bio_size = queue_max_hw_sectors(mdev->ldev->backing_bdev->bd_disk->queue) << 9;
- max_bio_size = min_t(int, max_bio_size, DRBD_MAX_BIO_SIZE);
+ max_bio_size = min(max_bio_size, DRBD_MAX_BIO_SIZE);
put_ldev(mdev);
} else {
d_size = 0;
@@ -2214,7 +2230,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
/* Never allow old drbd (up to 8.3.7) to see more than 32KiB */
if (mdev->agreed_pro_version <= 94)
- max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+ max_bio_size = min(max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
p.d_size = cpu_to_be64(d_size);
p.u_size = cpu_to_be64(u_size);
@@ -3541,6 +3557,22 @@ static int drbd_congested(void *congested_data, int bdi_bits)
goto out;
}
+ if (test_bit(CALLBACK_PENDING, &mdev->flags)) {
+ r |= (1 << BDI_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(mdev, D_UP_TO_DATE))
+ r |= (1 << BDI_sync_congested);
+ else
+ put_ldev(mdev);
+ r &= bdi_bits;
+ reason = 'c';
+ goto out;
+ }
+
if (get_ldev(mdev)) {
q = bdev_get_queue(mdev->ldev->backing_bdev);
r = bdi_congested(&q->backing_dev_info, bdi_bits);
@@ -3604,6 +3636,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
q->backing_dev_info.congested_data = mdev;
blk_queue_make_request(q, drbd_make_request);
+ blk_queue_flush(q, REQ_FLUSH | REQ_FUA);
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
@@ -3870,7 +3903,7 @@ void drbd_md_sync(struct drbd_conf *mdev)
if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
/* this was a try anyways ... */
dev_err(DEV, "meta data update failed!\n");
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
}
/* Update mdev->ldev->md.la_size_sect,
@@ -3950,9 +3983,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
spin_lock_irq(&mdev->req_lock);
if (mdev->state.conn < C_CONNECTED) {
- int peer;
+ unsigned int peer;
peer = be32_to_cpu(buffer->la_peer_max_bio_size);
- peer = max_t(int, peer, DRBD_MAX_BIO_SIZE_SAFE);
+ peer = max(peer, DRBD_MAX_BIO_SIZE_SAFE);
mdev->peer_max_bio_size = peer;
}
spin_unlock_irq(&mdev->req_lock);
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 6d4de6a72e80..fb9dce8daa24 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -147,6 +147,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
char *argv[] = {usermode_helper, cmd, mb, NULL };
int ret;
+ if (current == mdev->worker.task)
+ set_bit(CALLBACK_PENDING, &mdev->flags);
+
snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
if (get_net_conf(mdev)) {
@@ -189,6 +192,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
usermode_helper, cmd, mb,
(ret >> 8) & 0xff, ret);
+ if (current == mdev->worker.task)
+ clear_bit(CALLBACK_PENDING, &mdev->flags);
+
if (ret < 0) /* Ignore any ERRNOs we got. */
ret = 0;
@@ -795,8 +801,8 @@ static int drbd_check_al_size(struct drbd_conf *mdev)
static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_size)
{
struct request_queue * const q = mdev->rq_queue;
- int max_hw_sectors = max_bio_size >> 9;
- int max_segments = 0;
+ unsigned int max_hw_sectors = max_bio_size >> 9;
+ unsigned int max_segments = 0;
if (get_ldev_if_state(mdev, D_ATTACHING)) {
struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue;
@@ -829,7 +835,7 @@ static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_
void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
{
- int now, new, local, peer;
+ unsigned int now, new, local, peer;
now = queue_max_hw_sectors(mdev->rq_queue) << 9;
local = mdev->local_max_bio_size; /* Eventually last known value, from volatile memory */
@@ -840,13 +846,14 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
mdev->local_max_bio_size = local;
put_ldev(mdev);
}
+ local = min(local, DRBD_MAX_BIO_SIZE);
/* We may ignore peer limits if the peer is modern enough.
Because new from 8.3.8 onwards the peer can use multiple
BIOs for a single peer_request */
if (mdev->state.conn >= C_CONNECTED) {
if (mdev->agreed_pro_version < 94) {
- peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+ peer = min(mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
/* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
} else if (mdev->agreed_pro_version == 94)
peer = DRBD_MAX_SIZE_H80_PACKET;
@@ -854,10 +861,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
peer = DRBD_MAX_BIO_SIZE;
}
- new = min_t(int, local, peer);
+ new = min(local, peer);
if (mdev->state.role == R_PRIMARY && new < now)
- dev_err(DEV, "ASSERT FAILED new < now; (%d < %d)\n", new, now);
+ dev_err(DEV, "ASSERT FAILED new < now; (%u < %u)\n", new, now);
if (new != now)
dev_info(DEV, "max BIO size = %u\n", new);
@@ -950,6 +957,14 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
* to realize a "hot spare" feature (not that I'd recommend that) */
wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt));
+ /* make sure there is no leftover from previous force-detach attempts */
+ clear_bit(FORCE_DETACH, &mdev->flags);
+
+ /* and no leftover from previously aborted resync or verify, either */
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+
/* allocation not in the IO path, cqueue thread context */
nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
if (!nbc) {
@@ -1345,6 +1360,7 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
}
if (dt.detach_force) {
+ set_bit(FORCE_DETACH, &mdev->flags);
drbd_force_state(mdev, NS(disk, D_FAILED));
reply->ret_code = SS_SUCCESS;
goto out;
@@ -1962,9 +1978,11 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
int retcode;
/* If there is still bitmap IO pending, probably because of a previous
- * resync just being finished, wait for it before requesting a new resync. */
+ * resync just being finished, wait for it before requesting a new resync.
+ * Also wait for it's after_state_ch(). */
drbd_suspend_io(mdev);
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+ drbd_flush_workqueue(mdev);
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
@@ -2003,9 +2021,11 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
int retcode;
/* If there is still bitmap IO pending, probably because of a previous
- * resync just being finished, wait for it before requesting a new resync. */
+ * resync just being finished, wait for it before requesting a new resync.
+ * Also wait for it's after_state_ch(). */
drbd_suspend_io(mdev);
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+ drbd_flush_workqueue(mdev);
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index 869bada2ed06..5496104f90b9 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -245,6 +245,9 @@ static int drbd_seq_show(struct seq_file *seq, void *v)
mdev->state.role == R_SECONDARY) {
seq_printf(seq, "%2d: cs:Unconfigured\n", i);
} else {
+ /* reset mdev->congestion_reason */
+ bdi_rw_congested(&mdev->rq_queue->backing_dev_info);
+
seq_printf(seq,
"%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n"
" ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index ea4836e0ae98..c74ca2df7431 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -277,6 +277,9 @@ static void drbd_pp_free(struct drbd_conf *mdev, struct page *page, int is_net)
atomic_t *a = is_net ? &mdev->pp_in_use_by_net : &mdev->pp_in_use;
int i;
+ if (page == NULL)
+ return;
+
if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE)*minor_count)
i = page_chain_free(page);
else {
@@ -316,7 +319,7 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
gfp_t gfp_mask) __must_hold(local)
{
struct drbd_epoch_entry *e;
- struct page *page;
+ struct page *page = NULL;
unsigned nr_pages = (data_size + PAGE_SIZE -1) >> PAGE_SHIFT;
if (drbd_insert_fault(mdev, DRBD_FAULT_AL_EE))
@@ -329,9 +332,11 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
return NULL;
}
- page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
- if (!page)
- goto fail;
+ if (data_size) {
+ page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
+ if (!page)
+ goto fail;
+ }
INIT_HLIST_NODE(&e->collision);
e->epoch = NULL;
@@ -1270,7 +1275,6 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
data_size -= dgs;
- ERR_IF(data_size == 0) return NULL;
ERR_IF(data_size & 0x1ff) return NULL;
ERR_IF(data_size > DRBD_MAX_BIO_SIZE) return NULL;
@@ -1291,6 +1295,9 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
if (!e)
return NULL;
+ if (!data_size)
+ return e;
+
ds = data_size;
page = e->pages;
page_chain_for_each(page) {
@@ -1715,6 +1722,10 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
dp_flags = be32_to_cpu(p->dp_flags);
rw |= wire_flags_to_bio(mdev, dp_flags);
+ if (e->pages == NULL) {
+ D_ASSERT(e->size == 0);
+ D_ASSERT(dp_flags & DP_FLUSH);
+ }
if (dp_flags & DP_MAY_SET_IN_SYNC)
e->flags |= EE_MAY_SET_IN_SYNC;
@@ -3801,11 +3812,18 @@ void drbd_free_tl_hash(struct drbd_conf *mdev)
mdev->ee_hash = NULL;
mdev->ee_hash_s = 0;
- /* paranoia code */
- for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++)
- if (h->first)
- dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n",
- (int)(h - mdev->tl_hash), h->first);
+ /* We may not have had the chance to wait for all locally pending
+ * application requests. The hlist_add_fake() prevents access after
+ * free on master bio completion. */
+ for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) {
+ struct drbd_request *req;
+ struct hlist_node *pos, *n;
+ hlist_for_each_entry_safe(req, pos, n, h, collision) {
+ hlist_del_init(&req->collision);
+ hlist_add_fake(&req->collision);
+ }
+ }
+
kfree(mdev->tl_hash);
mdev->tl_hash = NULL;
mdev->tl_hash_s = 0;
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 8e93a6ac9bb6..910335c30927 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -455,7 +455,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
req->rq_state |= RQ_LOCAL_COMPLETED;
req->rq_state &= ~RQ_LOCAL_PENDING;
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
_req_may_be_done_not_susp(req, m);
break;
@@ -477,7 +477,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
break;
}
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
goto_queue_for_net_read:
@@ -1111,13 +1111,12 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
/*
* what we "blindly" assume:
*/
- D_ASSERT(bio->bi_size > 0);
D_ASSERT((bio->bi_size & 0x1ff) == 0);
/* to make some things easier, force alignment of requests within the
* granularity of our hash tables */
s_enr = bio->bi_sector >> HT_SHIFT;
- e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
+ e_enr = bio->bi_size ? (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT : s_enr;
if (likely(s_enr == e_enr)) {
do {
@@ -1275,7 +1274,7 @@ void request_timer_fn(unsigned long data)
time_after(now, req->start_time + dt) &&
!time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) {
dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n");
- __drbd_chk_io_error(mdev, 1);
+ __drbd_chk_io_error(mdev, DRBD_FORCE_DETACH);
}
nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
spin_unlock_irq(&mdev->req_lock);
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 620c70ff2231..6bce2cc179d4 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -111,7 +111,7 @@ void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local)
if (list_empty(&mdev->read_ee))
wake_up(&mdev->ee_wait);
if (test_bit(__EE_WAS_ERROR, &e->flags))
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
spin_unlock_irqrestore(&mdev->req_lock, flags);
drbd_queue_work(&mdev->data.work, &e->w);
@@ -154,7 +154,7 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo
: list_empty(&mdev->active_ee);
if (test_bit(__EE_WAS_ERROR, &e->flags))
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
spin_unlock_irqrestore(&mdev->req_lock, flags);
if (is_syncer_req)
@@ -1501,14 +1501,6 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
return;
}
- if (mdev->state.conn < C_AHEAD) {
- /* In case a previous resync run was aborted by an IO error/detach on the peer. */
- drbd_rs_cancel_all(mdev);
- /* This should be done when we abort the resync. We definitely do not
- want to have this for connections going back and forth between
- Ahead/Behind and SyncSource/SyncTarget */
- }
-
if (side == C_SYNC_TARGET) {
/* Since application IO was locked out during C_WF_BITMAP_T and
C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 553f43a90953..a7d6347aaa79 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -191,6 +191,7 @@ static int print_unex = 1;
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/async.h>
/*
* PS/2 floppies have much slower step rates than regular floppies.
@@ -2516,8 +2517,7 @@ static int make_raw_rw_request(void)
set_fdc((long)current_req->rq_disk->private_data);
raw_cmd = &default_raw_cmd;
- raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
- FD_RAW_NEED_SEEK;
+ raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
raw_cmd->cmd_count = NR_RW;
if (rq_data_dir(current_req) == READ) {
raw_cmd->flags |= FD_RAW_READ;
@@ -4123,7 +4123,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
return get_disk(disks[drive]);
}
-static int __init floppy_init(void)
+static int __init do_floppy_init(void)
{
int i, unit, drive;
int err, dr;
@@ -4338,6 +4338,24 @@ out_put_disk:
return err;
}
+#ifndef MODULE
+static __init void floppy_async_init(void *data, async_cookie_t cookie)
+{
+ do_floppy_init();
+}
+#endif
+
+static int __init floppy_init(void)
+{
+#ifdef MODULE
+ return do_floppy_init();
+#else
+ /* Don't hold up the bootup by the floppy initialization */
+ async_schedule(floppy_async_init, NULL);
+ return 0;
+#endif
+}
+
static const struct io_region {
int offset;
int size;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 061427a75d37..d07c9f7fded6 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -154,6 +154,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
struct msghdr msg;
struct kvec iov;
sigset_t blocked, oldset;
+ unsigned long pflags = current->flags;
if (unlikely(!sock)) {
dev_err(disk_to_dev(nbd->disk),
@@ -167,8 +168,9 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
siginitsetinv(&blocked, sigmask(SIGKILL));
sigprocmask(SIG_SETMASK, &blocked, &oldset);
+ current->flags |= PF_MEMALLOC;
do {
- sock->sk->sk_allocation = GFP_NOIO;
+ sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
iov.iov_base = buf;
iov.iov_len = size;
msg.msg_name = NULL;
@@ -214,6 +216,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
} while (size > 0);
sigprocmask(SIG_SETMASK, &oldset, NULL);
+ tsk_restore_flags(current, pflags, PF_MEMALLOC);
return result;
}
@@ -405,6 +408,7 @@ static int nbd_do_it(struct nbd_device *nbd)
BUG_ON(nbd->magic != NBD_MAGIC);
+ sk_set_memalloc(nbd->sock->sk);
nbd->pid = task_pid_nr(current);
ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr);
if (ret) {
@@ -481,7 +485,7 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req)
nbd_end_request(req);
} else {
spin_lock(&nbd->queue_lock);
- list_add(&req->queuelist, &nbd->queue_head);
+ list_add_tail(&req->queuelist, &nbd->queue_head);
spin_unlock(&nbd->queue_lock);
}
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 8f428a8ab003..9917943a3572 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -55,8 +55,6 @@
#define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */
-#define RBD_MAX_MD_NAME_LEN (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX))
-#define RBD_MAX_POOL_NAME_LEN 64
#define RBD_MAX_SNAP_NAME_LEN 32
#define RBD_MAX_OPT_LEN 1024
@@ -78,13 +76,12 @@
*/
struct rbd_image_header {
u64 image_size;
- char block_name[32];
+ char *object_prefix;
__u8 obj_order;
__u8 crypt_type;
__u8 comp_type;
struct ceph_snap_context *snapc;
size_t snap_names_len;
- u64 snap_seq;
u32 total_snaps;
char *snap_names;
@@ -150,7 +147,7 @@ struct rbd_snap {
* a single device
*/
struct rbd_device {
- int id; /* blkdev unique id */
+ int dev_id; /* blkdev unique id */
int major; /* blkdev assigned major */
struct gendisk *disk; /* blkdev's gendisk and rq */
@@ -163,20 +160,24 @@ struct rbd_device {
spinlock_t lock; /* queue lock */
struct rbd_image_header header;
- char obj[RBD_MAX_OBJ_NAME_LEN]; /* rbd image name */
- int obj_len;
- char obj_md_name[RBD_MAX_MD_NAME_LEN]; /* hdr nm. */
- char pool_name[RBD_MAX_POOL_NAME_LEN];
- int poolid;
+ char *image_name;
+ size_t image_name_len;
+ char *header_name;
+ char *pool_name;
+ int pool_id;
struct ceph_osd_event *watch_event;
struct ceph_osd_request *watch_request;
/* protects updating the header */
struct rw_semaphore header_rwsem;
- char snap_name[RBD_MAX_SNAP_NAME_LEN];
+ /* name of the snapshot this device reads from */
+ char *snap_name;
+ /* id of the snapshot this device reads from */
u64 snap_id; /* current snapshot id */
- int read_only;
+ /* whether the snap_id this device reads from still exists */
+ bool snap_exists;
+ int read_only;
struct list_head node;
@@ -201,8 +202,7 @@ static ssize_t rbd_snap_add(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count);
-static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
- struct rbd_snap *snap);
+static void __rbd_remove_snap_dev(struct rbd_snap *snap);
static ssize_t rbd_add(struct bus_type *bus, const char *buf,
size_t count);
@@ -240,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
put_device(&rbd_dev->dev);
}
-static int __rbd_refresh_header(struct rbd_device *rbd_dev);
+static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver);
static int rbd_open(struct block_device *bdev, fmode_t mode)
{
@@ -273,9 +273,9 @@ static const struct block_device_operations rbd_bd_ops = {
/*
* Initialize an rbd client instance.
- * We own *opt.
+ * We own *ceph_opts.
*/
-static struct rbd_client *rbd_client_create(struct ceph_options *opt,
+static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts,
struct rbd_options *rbd_opts)
{
struct rbd_client *rbdc;
@@ -291,10 +291,10 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt,
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- rbdc->client = ceph_create_client(opt, rbdc, 0, 0);
+ rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0);
if (IS_ERR(rbdc->client))
goto out_mutex;
- opt = NULL; /* Now rbdc->client is responsible for opt */
+ ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */
ret = ceph_open_session(rbdc->client);
if (ret < 0)
@@ -317,23 +317,23 @@ out_mutex:
mutex_unlock(&ctl_mutex);
kfree(rbdc);
out_opt:
- if (opt)
- ceph_destroy_options(opt);
+ if (ceph_opts)
+ ceph_destroy_options(ceph_opts);
return ERR_PTR(ret);
}
/*
* Find a ceph client with specific addr and configuration.
*/
-static struct rbd_client *__rbd_client_find(struct ceph_options *opt)
+static struct rbd_client *__rbd_client_find(struct ceph_options *ceph_opts)
{
struct rbd_client *client_node;
- if (opt->flags & CEPH_OPT_NOSHARE)
+ if (ceph_opts->flags & CEPH_OPT_NOSHARE)
return NULL;
list_for_each_entry(client_node, &rbd_client_list, node)
- if (ceph_compare_options(opt, client_node->client) == 0)
+ if (!ceph_compare_options(ceph_opts, client_node->client))
return client_node;
return NULL;
}
@@ -349,7 +349,7 @@ enum {
/* string args above */
};
-static match_table_t rbdopt_tokens = {
+static match_table_t rbd_opts_tokens = {
{Opt_notify_timeout, "notify_timeout=%d"},
/* int args above */
/* string args above */
@@ -358,11 +358,11 @@ static match_table_t rbdopt_tokens = {
static int parse_rbd_opts_token(char *c, void *private)
{
- struct rbd_options *rbdopt = private;
+ struct rbd_options *rbd_opts = private;
substring_t argstr[MAX_OPT_ARGS];
int token, intval, ret;
- token = match_token(c, rbdopt_tokens, argstr);
+ token = match_token(c, rbd_opts_tokens, argstr);
if (token < 0)
return -EINVAL;
@@ -383,7 +383,7 @@ static int parse_rbd_opts_token(char *c, void *private)
switch (token) {
case Opt_notify_timeout:
- rbdopt->notify_timeout = intval;
+ rbd_opts->notify_timeout = intval;
break;
default:
BUG_ON(token);
@@ -400,7 +400,7 @@ static struct rbd_client *rbd_get_client(const char *mon_addr,
char *options)
{
struct rbd_client *rbdc;
- struct ceph_options *opt;
+ struct ceph_options *ceph_opts;
struct rbd_options *rbd_opts;
rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL);
@@ -409,29 +409,29 @@ static struct rbd_client *rbd_get_client(const char *mon_addr,
rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT;
- opt = ceph_parse_options(options, mon_addr,
- mon_addr + mon_addr_len,
- parse_rbd_opts_token, rbd_opts);
- if (IS_ERR(opt)) {
+ ceph_opts = ceph_parse_options(options, mon_addr,
+ mon_addr + mon_addr_len,
+ parse_rbd_opts_token, rbd_opts);
+ if (IS_ERR(ceph_opts)) {
kfree(rbd_opts);
- return ERR_CAST(opt);
+ return ERR_CAST(ceph_opts);
}
spin_lock(&rbd_client_list_lock);
- rbdc = __rbd_client_find(opt);
+ rbdc = __rbd_client_find(ceph_opts);
if (rbdc) {
/* using an existing client */
kref_get(&rbdc->kref);
spin_unlock(&rbd_client_list_lock);
- ceph_destroy_options(opt);
+ ceph_destroy_options(ceph_opts);
kfree(rbd_opts);
return rbdc;
}
spin_unlock(&rbd_client_list_lock);
- rbdc = rbd_client_create(opt, rbd_opts);
+ rbdc = rbd_client_create(ceph_opts, rbd_opts);
if (IS_ERR(rbdc))
kfree(rbd_opts);
@@ -480,46 +480,60 @@ static void rbd_coll_release(struct kref *kref)
kfree(coll);
}
+static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk)
+{
+ return !memcmp(&ondisk->text,
+ RBD_HEADER_TEXT, sizeof (RBD_HEADER_TEXT));
+}
+
/*
* Create a new header structure, translate header format from the on-disk
* header.
*/
static int rbd_header_from_disk(struct rbd_image_header *header,
struct rbd_image_header_ondisk *ondisk,
- u32 allocated_snaps,
- gfp_t gfp_flags)
+ u32 allocated_snaps)
{
- u32 i, snap_count;
+ u32 snap_count;
- if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT)))
+ if (!rbd_dev_ondisk_valid(ondisk))
return -ENXIO;
snap_count = le32_to_cpu(ondisk->snap_count);
- if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context))
- / sizeof (*ondisk))
+ if (snap_count > (SIZE_MAX - sizeof(struct ceph_snap_context))
+ / sizeof (u64))
return -EINVAL;
header->snapc = kmalloc(sizeof(struct ceph_snap_context) +
snap_count * sizeof(u64),
- gfp_flags);
+ GFP_KERNEL);
if (!header->snapc)
return -ENOMEM;
- header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
if (snap_count) {
+ header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
header->snap_names = kmalloc(header->snap_names_len,
- gfp_flags);
+ GFP_KERNEL);
if (!header->snap_names)
goto err_snapc;
header->snap_sizes = kmalloc(snap_count * sizeof(u64),
- gfp_flags);
+ GFP_KERNEL);
if (!header->snap_sizes)
goto err_names;
} else {
+ WARN_ON(ondisk->snap_names_len);
+ header->snap_names_len = 0;
header->snap_names = NULL;
header->snap_sizes = NULL;
}
- memcpy(header->block_name, ondisk->block_name,
+
+ header->object_prefix = kmalloc(sizeof (ondisk->block_name) + 1,
+ GFP_KERNEL);
+ if (!header->object_prefix)
+ goto err_sizes;
+
+ memcpy(header->object_prefix, ondisk->block_name,
sizeof(ondisk->block_name));
+ header->object_prefix[sizeof (ondisk->block_name)] = '\0';
header->image_size = le64_to_cpu(ondisk->image_size);
header->obj_order = ondisk->options.order;
@@ -527,11 +541,13 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
header->comp_type = ondisk->options.comp_type;
atomic_set(&header->snapc->nref, 1);
- header->snap_seq = le64_to_cpu(ondisk->snap_seq);
+ header->snapc->seq = le64_to_cpu(ondisk->snap_seq);
header->snapc->num_snaps = snap_count;
header->total_snaps = snap_count;
if (snap_count && allocated_snaps == snap_count) {
+ int i;
+
for (i = 0; i < snap_count; i++) {
header->snapc->snaps[i] =
le64_to_cpu(ondisk->snaps[i].id);
@@ -540,16 +556,22 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
}
/* copy snapshot names */
- memcpy(header->snap_names, &ondisk->snaps[i],
+ memcpy(header->snap_names, &ondisk->snaps[snap_count],
header->snap_names_len);
}
return 0;
+err_sizes:
+ kfree(header->snap_sizes);
+ header->snap_sizes = NULL;
err_names:
kfree(header->snap_names);
+ header->snap_names = NULL;
err_snapc:
kfree(header->snapc);
+ header->snapc = NULL;
+
return -ENOMEM;
}
@@ -575,52 +597,50 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name,
return -ENOENT;
}
-static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
+static int rbd_header_set_snap(struct rbd_device *rbd_dev, u64 *size)
{
- struct rbd_image_header *header = &dev->header;
- struct ceph_snap_context *snapc = header->snapc;
- int ret = -ENOENT;
-
- BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME));
+ int ret;
- down_write(&dev->header_rwsem);
+ down_write(&rbd_dev->header_rwsem);
- if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME,
+ if (!memcmp(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
sizeof (RBD_SNAP_HEAD_NAME))) {
- if (header->total_snaps)
- snapc->seq = header->snap_seq;
- else
- snapc->seq = 0;
- dev->snap_id = CEPH_NOSNAP;
- dev->read_only = 0;
+ rbd_dev->snap_id = CEPH_NOSNAP;
+ rbd_dev->snap_exists = false;
+ rbd_dev->read_only = 0;
if (size)
- *size = header->image_size;
+ *size = rbd_dev->header.image_size;
} else {
- ret = snap_by_name(header, dev->snap_name, &snapc->seq, size);
+ u64 snap_id = 0;
+
+ ret = snap_by_name(&rbd_dev->header, rbd_dev->snap_name,
+ &snap_id, size);
if (ret < 0)
goto done;
- dev->snap_id = snapc->seq;
- dev->read_only = 1;
+ rbd_dev->snap_id = snap_id;
+ rbd_dev->snap_exists = true;
+ rbd_dev->read_only = 1;
}
ret = 0;
done:
- up_write(&dev->header_rwsem);
+ up_write(&rbd_dev->header_rwsem);
return ret;
}
static void rbd_header_free(struct rbd_image_header *header)
{
- kfree(header->snapc);
- kfree(header->snap_names);
+ kfree(header->object_prefix);
kfree(header->snap_sizes);
+ kfree(header->snap_names);
+ ceph_put_snap_context(header->snapc);
}
/*
* get the actual striped segment name, offset and length
*/
static u64 rbd_get_segment(struct rbd_image_header *header,
- const char *block_name,
+ const char *object_prefix,
u64 ofs, u64 len,
char *seg_name, u64 *segofs)
{
@@ -628,7 +648,7 @@ static u64 rbd_get_segment(struct rbd_image_header *header,
if (seg_name)
snprintf(seg_name, RBD_MAX_SEG_NAME_LEN,
- "%s.%012llx", block_name, seg);
+ "%s.%012llx", object_prefix, seg);
ofs = ofs & ((1 << header->obj_order) - 1);
len = min_t(u64, len, (1 << header->obj_order) - ofs);
@@ -726,9 +746,8 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next,
* split_bio will BUG_ON if this is not the case
*/
dout("bio_chain_clone split! total=%d remaining=%d"
- "bi_size=%d\n",
- (int)total, (int)len-total,
- (int)old_chain->bi_size);
+ "bi_size=%u\n",
+ total, len - total, old_chain->bi_size);
/* split the bio. We'll release it either in the next
call, or it will have to be released outside */
@@ -777,22 +796,24 @@ err_out:
/*
* helpers for osd request op vectors.
*/
-static int rbd_create_rw_ops(struct ceph_osd_req_op **ops,
- int num_ops,
- int opcode,
- u32 payload_len)
-{
- *ops = kzalloc(sizeof(struct ceph_osd_req_op) * (num_ops + 1),
- GFP_NOIO);
- if (!*ops)
- return -ENOMEM;
- (*ops)[0].op = opcode;
+static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops,
+ int opcode, u32 payload_len)
+{
+ struct ceph_osd_req_op *ops;
+
+ ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO);
+ if (!ops)
+ return NULL;
+
+ ops[0].op = opcode;
+
/*
* op extent offset and length will be set later on
* in calc_raw_layout()
*/
- (*ops)[0].payload_len = payload_len;
- return 0;
+ ops[0].payload_len = payload_len;
+
+ return ops;
}
static void rbd_destroy_ops(struct ceph_osd_req_op *ops)
@@ -808,8 +829,8 @@ static void rbd_coll_end_req_index(struct request *rq,
struct request_queue *q;
int min, max, i;
- dout("rbd_coll_end_req_index %p index %d ret %d len %lld\n",
- coll, index, ret, len);
+ dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n",
+ coll, index, ret, (unsigned long long) len);
if (!rq)
return;
@@ -848,16 +869,15 @@ static void rbd_coll_end_req(struct rbd_request *req,
* Send ceph osd request
*/
static int rbd_do_request(struct request *rq,
- struct rbd_device *dev,
+ struct rbd_device *rbd_dev,
struct ceph_snap_context *snapc,
u64 snapid,
- const char *obj, u64 ofs, u64 len,
+ const char *object_name, u64 ofs, u64 len,
struct bio *bio,
struct page **pages,
int num_pages,
int flags,
struct ceph_osd_req_op *ops,
- int num_reply,
struct rbd_req_coll *coll,
int coll_index,
void (*rbd_cb)(struct ceph_osd_request *req,
@@ -887,15 +907,13 @@ static int rbd_do_request(struct request *rq,
req_data->coll_index = coll_index;
}
- dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs);
-
- down_read(&dev->header_rwsem);
+ dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name,
+ (unsigned long long) ofs, (unsigned long long) len);
- osdc = &dev->rbd_client->client->osdc;
+ osdc = &rbd_dev->rbd_client->client->osdc;
req = ceph_osdc_alloc_request(osdc, flags, snapc, ops,
false, GFP_NOIO, pages, bio);
if (!req) {
- up_read(&dev->header_rwsem);
ret = -ENOMEM;
goto done_pages;
}
@@ -912,7 +930,7 @@ static int rbd_do_request(struct request *rq,
reqhead = req->r_request->front.iov_base;
reqhead->snapid = cpu_to_le64(CEPH_NOSNAP);
- strncpy(req->r_oid, obj, sizeof(req->r_oid));
+ strncpy(req->r_oid, object_name, sizeof(req->r_oid));
req->r_oid_len = strlen(req->r_oid);
layout = &req->r_file_layout;
@@ -920,7 +938,7 @@ static int rbd_do_request(struct request *rq,
layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
layout->fl_stripe_count = cpu_to_le32(1);
layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
- layout->fl_pg_pool = cpu_to_le32(dev->poolid);
+ layout->fl_pg_pool = cpu_to_le32(rbd_dev->pool_id);
ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
req, ops);
@@ -929,7 +947,6 @@ static int rbd_do_request(struct request *rq,
snapc,
&mtime,
req->r_oid, req->r_oid_len);
- up_read(&dev->header_rwsem);
if (linger_req) {
ceph_osdc_set_request_linger(osdc, req);
@@ -944,8 +961,9 @@ static int rbd_do_request(struct request *rq,
ret = ceph_osdc_wait_request(osdc, req);
if (ver)
*ver = le64_to_cpu(req->r_reassert_version.version);
- dout("reassert_ver=%lld\n",
- le64_to_cpu(req->r_reassert_version.version));
+ dout("reassert_ver=%llu\n",
+ (unsigned long long)
+ le64_to_cpu(req->r_reassert_version.version));
ceph_osdc_put_request(req);
}
return ret;
@@ -979,7 +997,8 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
bytes = le64_to_cpu(op->extent.length);
read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ);
- dout("rbd_req_cb bytes=%lld readop=%d rc=%d\n", bytes, read_op, rc);
+ dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n",
+ (unsigned long long) bytes, read_op, (int) rc);
if (rc == -ENOENT && read_op) {
zero_bio_chain(req_data->bio, 0);
@@ -1006,14 +1025,12 @@ static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg
/*
* Do a synchronous ceph osd operation
*/
-static int rbd_req_sync_op(struct rbd_device *dev,
+static int rbd_req_sync_op(struct rbd_device *rbd_dev,
struct ceph_snap_context *snapc,
u64 snapid,
- int opcode,
int flags,
- struct ceph_osd_req_op *orig_ops,
- int num_reply,
- const char *obj,
+ struct ceph_osd_req_op *ops,
+ const char *object_name,
u64 ofs, u64 len,
char *buf,
struct ceph_osd_request **linger_req,
@@ -1022,45 +1039,28 @@ static int rbd_req_sync_op(struct rbd_device *dev,
int ret;
struct page **pages;
int num_pages;
- struct ceph_osd_req_op *ops = orig_ops;
- u32 payload_len;
+
+ BUG_ON(ops == NULL);
num_pages = calc_pages_for(ofs , len);
pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL);
if (IS_ERR(pages))
return PTR_ERR(pages);
- if (!orig_ops) {
- payload_len = (flags & CEPH_OSD_FLAG_WRITE ? len : 0);
- ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len);
- if (ret < 0)
- goto done;
-
- if ((flags & CEPH_OSD_FLAG_WRITE) && buf) {
- ret = ceph_copy_to_page_vector(pages, buf, ofs, len);
- if (ret < 0)
- goto done_ops;
- }
- }
-
- ret = rbd_do_request(NULL, dev, snapc, snapid,
- obj, ofs, len, NULL,
+ ret = rbd_do_request(NULL, rbd_dev, snapc, snapid,
+ object_name, ofs, len, NULL,
pages, num_pages,
flags,
ops,
- 2,
NULL, 0,
NULL,
linger_req, ver);
if (ret < 0)
- goto done_ops;
+ goto done;
if ((flags & CEPH_OSD_FLAG_READ) && buf)
ret = ceph_copy_from_page_vector(pages, buf, ofs, ret);
-done_ops:
- if (!orig_ops)
- rbd_destroy_ops(ops);
done:
ceph_release_page_vector(pages, num_pages);
return ret;
@@ -1070,10 +1070,10 @@ done:
* Do an asynchronous ceph osd operation
*/
static int rbd_do_op(struct request *rq,
- struct rbd_device *rbd_dev ,
+ struct rbd_device *rbd_dev,
struct ceph_snap_context *snapc,
u64 snapid,
- int opcode, int flags, int num_reply,
+ int opcode, int flags,
u64 ofs, u64 len,
struct bio *bio,
struct rbd_req_coll *coll,
@@ -1091,14 +1091,15 @@ static int rbd_do_op(struct request *rq,
return -ENOMEM;
seg_len = rbd_get_segment(&rbd_dev->header,
- rbd_dev->header.block_name,
+ rbd_dev->header.object_prefix,
ofs, len,
seg_name, &seg_ofs);
payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0);
- ret = rbd_create_rw_ops(&ops, 1, opcode, payload_len);
- if (ret < 0)
+ ret = -ENOMEM;
+ ops = rbd_create_rw_ops(1, opcode, payload_len);
+ if (!ops)
goto done;
/* we've taken care of segment sizes earlier when we
@@ -1112,7 +1113,6 @@ static int rbd_do_op(struct request *rq,
NULL, 0,
flags,
ops,
- num_reply,
coll, coll_index,
rbd_req_cb, 0, NULL);
@@ -1136,7 +1136,6 @@ static int rbd_req_write(struct request *rq,
return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP,
CEPH_OSD_OP_WRITE,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
- 2,
ofs, len, bio, coll, coll_index);
}
@@ -1155,55 +1154,58 @@ static int rbd_req_read(struct request *rq,
snapid,
CEPH_OSD_OP_READ,
CEPH_OSD_FLAG_READ,
- 2,
ofs, len, bio, coll, coll_index);
}
/*
* Request sync osd read
*/
-static int rbd_req_sync_read(struct rbd_device *dev,
- struct ceph_snap_context *snapc,
+static int rbd_req_sync_read(struct rbd_device *rbd_dev,
u64 snapid,
- const char *obj,
+ const char *object_name,
u64 ofs, u64 len,
char *buf,
u64 *ver)
{
- return rbd_req_sync_op(dev, NULL,
+ struct ceph_osd_req_op *ops;
+ int ret;
+
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0);
+ if (!ops)
+ return -ENOMEM;
+
+ ret = rbd_req_sync_op(rbd_dev, NULL,
snapid,
- CEPH_OSD_OP_READ,
CEPH_OSD_FLAG_READ,
- NULL,
- 1, obj, ofs, len, buf, NULL, ver);
+ ops, object_name, ofs, len, buf, NULL, ver);
+ rbd_destroy_ops(ops);
+
+ return ret;
}
/*
* Request sync osd watch
*/
-static int rbd_req_sync_notify_ack(struct rbd_device *dev,
+static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev,
u64 ver,
- u64 notify_id,
- const char *obj)
+ u64 notify_id)
{
struct ceph_osd_req_op *ops;
- struct page **pages = NULL;
int ret;
- ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
- if (ret < 0)
- return ret;
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0);
+ if (!ops)
+ return -ENOMEM;
- ops[0].watch.ver = cpu_to_le64(dev->header.obj_version);
+ ops[0].watch.ver = cpu_to_le64(ver);
ops[0].watch.cookie = notify_id;
ops[0].watch.flag = 0;
- ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP,
- obj, 0, 0, NULL,
- pages, 0,
+ ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP,
+ rbd_dev->header_name, 0, 0, NULL,
+ NULL, 0,
CEPH_OSD_FLAG_READ,
ops,
- 1,
NULL, 0,
rbd_simple_req_cb, 0, NULL);
@@ -1213,54 +1215,53 @@ static int rbd_req_sync_notify_ack(struct rbd_device *dev,
static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
{
- struct rbd_device *dev = (struct rbd_device *)data;
+ struct rbd_device *rbd_dev = (struct rbd_device *)data;
+ u64 hver;
int rc;
- if (!dev)
+ if (!rbd_dev)
return;
- dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
- notify_id, (int)opcode);
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- rc = __rbd_refresh_header(dev);
- mutex_unlock(&ctl_mutex);
+ dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n",
+ rbd_dev->header_name, (unsigned long long) notify_id,
+ (unsigned int) opcode);
+ rc = rbd_refresh_header(rbd_dev, &hver);
if (rc)
pr_warning(RBD_DRV_NAME "%d got notification but failed to "
- " update snaps: %d\n", dev->major, rc);
+ " update snaps: %d\n", rbd_dev->major, rc);
- rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name);
+ rbd_req_sync_notify_ack(rbd_dev, hver, notify_id);
}
/*
* Request sync osd watch
*/
-static int rbd_req_sync_watch(struct rbd_device *dev,
- const char *obj,
- u64 ver)
+static int rbd_req_sync_watch(struct rbd_device *rbd_dev)
{
struct ceph_osd_req_op *ops;
- struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
+ struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+ int ret;
- int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
- if (ret < 0)
- return ret;
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0);
+ if (!ops)
+ return -ENOMEM;
ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0,
- (void *)dev, &dev->watch_event);
+ (void *)rbd_dev, &rbd_dev->watch_event);
if (ret < 0)
goto fail;
- ops[0].watch.ver = cpu_to_le64(ver);
- ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+ ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version);
+ ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie);
ops[0].watch.flag = 1;
- ret = rbd_req_sync_op(dev, NULL,
+ ret = rbd_req_sync_op(rbd_dev, NULL,
CEPH_NOSNAP,
- 0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
- 1, obj, 0, 0, NULL,
- &dev->watch_request, NULL);
+ rbd_dev->header_name,
+ 0, 0, NULL,
+ &rbd_dev->watch_request, NULL);
if (ret < 0)
goto fail_event;
@@ -1269,8 +1270,8 @@ static int rbd_req_sync_watch(struct rbd_device *dev,
return 0;
fail_event:
- ceph_osdc_cancel_event(dev->watch_event);
- dev->watch_event = NULL;
+ ceph_osdc_cancel_event(rbd_dev->watch_event);
+ rbd_dev->watch_event = NULL;
fail:
rbd_destroy_ops(ops);
return ret;
@@ -1279,64 +1280,65 @@ fail:
/*
* Request sync osd unwatch
*/
-static int rbd_req_sync_unwatch(struct rbd_device *dev,
- const char *obj)
+static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev)
{
struct ceph_osd_req_op *ops;
+ int ret;
- int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
- if (ret < 0)
- return ret;
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0);
+ if (!ops)
+ return -ENOMEM;
ops[0].watch.ver = 0;
- ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
+ ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie);
ops[0].watch.flag = 0;
- ret = rbd_req_sync_op(dev, NULL,
+ ret = rbd_req_sync_op(rbd_dev, NULL,
CEPH_NOSNAP,
- 0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
- 1, obj, 0, 0, NULL, NULL, NULL);
+ rbd_dev->header_name,
+ 0, 0, NULL, NULL, NULL);
+
rbd_destroy_ops(ops);
- ceph_osdc_cancel_event(dev->watch_event);
- dev->watch_event = NULL;
+ ceph_osdc_cancel_event(rbd_dev->watch_event);
+ rbd_dev->watch_event = NULL;
return ret;
}
struct rbd_notify_info {
- struct rbd_device *dev;
+ struct rbd_device *rbd_dev;
};
static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
{
- struct rbd_device *dev = (struct rbd_device *)data;
- if (!dev)
+ struct rbd_device *rbd_dev = (struct rbd_device *)data;
+ if (!rbd_dev)
return;
- dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
- notify_id, (int)opcode);
+ dout("rbd_notify_cb %s notify_id=%llu opcode=%u\n",
+ rbd_dev->header_name, (unsigned long long) notify_id,
+ (unsigned int) opcode);
}
/*
* Request sync osd notify
*/
-static int rbd_req_sync_notify(struct rbd_device *dev,
- const char *obj)
+static int rbd_req_sync_notify(struct rbd_device *rbd_dev)
{
struct ceph_osd_req_op *ops;
- struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
+ struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct ceph_osd_event *event;
struct rbd_notify_info info;
int payload_len = sizeof(u32) + sizeof(u32);
int ret;
- ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len);
- if (ret < 0)
- return ret;
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY, payload_len);
+ if (!ops)
+ return -ENOMEM;
- info.dev = dev;
+ info.rbd_dev = rbd_dev;
ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1,
(void *)&info, &event);
@@ -1349,12 +1351,12 @@ static int rbd_req_sync_notify(struct rbd_device *dev,
ops[0].watch.prot_ver = RADOS_NOTIFY_VER;
ops[0].watch.timeout = 12;
- ret = rbd_req_sync_op(dev, NULL,
+ ret = rbd_req_sync_op(rbd_dev, NULL,
CEPH_NOSNAP,
- 0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
- 1, obj, 0, 0, NULL, NULL, NULL);
+ rbd_dev->header_name,
+ 0, 0, NULL, NULL, NULL);
if (ret < 0)
goto fail_event;
@@ -1373,36 +1375,37 @@ fail:
/*
* Request sync osd read
*/
-static int rbd_req_sync_exec(struct rbd_device *dev,
- const char *obj,
- const char *cls,
- const char *method,
+static int rbd_req_sync_exec(struct rbd_device *rbd_dev,
+ const char *object_name,
+ const char *class_name,
+ const char *method_name,
const char *data,
int len,
u64 *ver)
{
struct ceph_osd_req_op *ops;
- int cls_len = strlen(cls);
- int method_len = strlen(method);
- int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_CALL,
- cls_len + method_len + len);
- if (ret < 0)
- return ret;
+ int class_name_len = strlen(class_name);
+ int method_name_len = strlen(method_name);
+ int ret;
- ops[0].cls.class_name = cls;
- ops[0].cls.class_len = (__u8)cls_len;
- ops[0].cls.method_name = method;
- ops[0].cls.method_len = (__u8)method_len;
+ ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL,
+ class_name_len + method_name_len + len);
+ if (!ops)
+ return -ENOMEM;
+
+ ops[0].cls.class_name = class_name;
+ ops[0].cls.class_len = (__u8) class_name_len;
+ ops[0].cls.method_name = method_name;
+ ops[0].cls.method_len = (__u8) method_name_len;
ops[0].cls.argc = 0;
ops[0].cls.indata = data;
ops[0].cls.indata_len = len;
- ret = rbd_req_sync_op(dev, NULL,
+ ret = rbd_req_sync_op(rbd_dev, NULL,
CEPH_NOSNAP,
- 0,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
ops,
- 1, obj, 0, 0, NULL, NULL, ver);
+ object_name, 0, 0, NULL, NULL, ver);
rbd_destroy_ops(ops);
@@ -1437,10 +1440,12 @@ static void rbd_rq_fn(struct request_queue *q)
struct bio *bio;
struct bio *rq_bio, *next_bio = NULL;
bool do_write;
- int size, op_size = 0;
+ unsigned int size;
+ u64 op_size = 0;
u64 ofs;
int num_segs, cur_seg = 0;
struct rbd_req_coll *coll;
+ struct ceph_snap_context *snapc;
/* peek at request from block layer */
if (!rq)
@@ -1467,23 +1472,38 @@ static void rbd_rq_fn(struct request_queue *q)
spin_unlock_irq(q->queue_lock);
+ down_read(&rbd_dev->header_rwsem);
+
+ if (rbd_dev->snap_id != CEPH_NOSNAP && !rbd_dev->snap_exists) {
+ up_read(&rbd_dev->header_rwsem);
+ dout("request for non-existent snapshot");
+ spin_lock_irq(q->queue_lock);
+ __blk_end_request_all(rq, -ENXIO);
+ continue;
+ }
+
+ snapc = ceph_get_snap_context(rbd_dev->header.snapc);
+
+ up_read(&rbd_dev->header_rwsem);
+
dout("%s 0x%x bytes at 0x%llx\n",
do_write ? "write" : "read",
- size, blk_rq_pos(rq) * SECTOR_SIZE);
+ size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE);
num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size);
coll = rbd_alloc_coll(num_segs);
if (!coll) {
spin_lock_irq(q->queue_lock);
__blk_end_request_all(rq, -ENOMEM);
+ ceph_put_snap_context(snapc);
continue;
}
do {
/* a bio clone to be passed down to OSD req */
- dout("rq->bio->bi_vcnt=%d\n", rq->bio->bi_vcnt);
+ dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt);
op_size = rbd_get_segment(&rbd_dev->header,
- rbd_dev->header.block_name,
+ rbd_dev->header.object_prefix,
ofs, size,
NULL, NULL);
kref_get(&coll->kref);
@@ -1499,7 +1519,7 @@ static void rbd_rq_fn(struct request_queue *q)
/* init OSD command: write or read */
if (do_write)
rbd_req_write(rq, rbd_dev,
- rbd_dev->header.snapc,
+ snapc,
ofs,
op_size, bio,
coll, cur_seg);
@@ -1522,6 +1542,8 @@ next_seg:
if (bp)
bio_pair_release(bp);
spin_lock_irq(q->queue_lock);
+
+ ceph_put_snap_context(snapc);
}
}
@@ -1592,18 +1614,19 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
return -ENOMEM;
rc = rbd_req_sync_read(rbd_dev,
- NULL, CEPH_NOSNAP,
- rbd_dev->obj_md_name,
+ CEPH_NOSNAP,
+ rbd_dev->header_name,
0, len,
(char *)dh, &ver);
if (rc < 0)
goto out_dh;
- rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL);
+ rc = rbd_header_from_disk(header, dh, snap_count);
if (rc < 0) {
if (rc == -ENXIO)
pr_warning("unrecognized header format"
- " for image %s", rbd_dev->obj);
+ " for image %s\n",
+ rbd_dev->image_name);
goto out_dh;
}
@@ -1628,7 +1651,7 @@ out_dh:
/*
* create a snapshot
*/
-static int rbd_header_add_snap(struct rbd_device *dev,
+static int rbd_header_add_snap(struct rbd_device *rbd_dev,
const char *snap_name,
gfp_t gfp_flags)
{
@@ -1636,16 +1659,15 @@ static int rbd_header_add_snap(struct rbd_device *dev,
u64 new_snapid;
int ret;
void *data, *p, *e;
- u64 ver;
struct ceph_mon_client *monc;
/* we should create a snapshot only if we're pointing at the head */
- if (dev->snap_id != CEPH_NOSNAP)
+ if (rbd_dev->snap_id != CEPH_NOSNAP)
return -EINVAL;
- monc = &dev->rbd_client->client->monc;
- ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid);
- dout("created snapid=%lld\n", new_snapid);
+ monc = &rbd_dev->rbd_client->client->monc;
+ ret = ceph_monc_create_snapid(monc, rbd_dev->pool_id, &new_snapid);
+ dout("created snapid=%llu\n", (unsigned long long) new_snapid);
if (ret < 0)
return ret;
@@ -1659,19 +1681,13 @@ static int rbd_header_add_snap(struct rbd_device *dev,
ceph_encode_string_safe(&p, e, snap_name, name_len, bad);
ceph_encode_64_safe(&p, e, new_snapid, bad);
- ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add",
- data, p - data, &ver);
+ ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name,
+ "rbd", "snap_add",
+ data, p - data, NULL);
kfree(data);
- if (ret < 0)
- return ret;
-
- down_write(&dev->header_rwsem);
- dev->header.snapc->seq = new_snapid;
- up_write(&dev->header_rwsem);
-
- return 0;
+ return ret < 0 ? ret : 0;
bad:
return -ERANGE;
}
@@ -1679,52 +1695,52 @@ bad:
static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev)
{
struct rbd_snap *snap;
+ struct rbd_snap *next;
- while (!list_empty(&rbd_dev->snaps)) {
- snap = list_first_entry(&rbd_dev->snaps, struct rbd_snap, node);
- __rbd_remove_snap_dev(rbd_dev, snap);
- }
+ list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node)
+ __rbd_remove_snap_dev(snap);
}
/*
* only read the first part of the ondisk header, without the snaps info
*/
-static int __rbd_refresh_header(struct rbd_device *rbd_dev)
+static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver)
{
int ret;
struct rbd_image_header h;
- u64 snap_seq;
- int follow_seq = 0;
ret = rbd_read_header(rbd_dev, &h);
if (ret < 0)
return ret;
- /* resized? */
- set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE);
-
down_write(&rbd_dev->header_rwsem);
- snap_seq = rbd_dev->header.snapc->seq;
- if (rbd_dev->header.total_snaps &&
- rbd_dev->header.snapc->snaps[0] == snap_seq)
- /* pointing at the head, will need to follow that
- if head moves */
- follow_seq = 1;
+ /* resized? */
+ if (rbd_dev->snap_id == CEPH_NOSNAP) {
+ sector_t size = (sector_t) h.image_size / SECTOR_SIZE;
- kfree(rbd_dev->header.snapc);
- kfree(rbd_dev->header.snap_names);
+ dout("setting size to %llu sectors", (unsigned long long) size);
+ set_capacity(rbd_dev->disk, size);
+ }
+
+ /* rbd_dev->header.object_prefix shouldn't change */
kfree(rbd_dev->header.snap_sizes);
+ kfree(rbd_dev->header.snap_names);
+ /* osd requests may still refer to snapc */
+ ceph_put_snap_context(rbd_dev->header.snapc);
+ if (hver)
+ *hver = h.obj_version;
+ rbd_dev->header.obj_version = h.obj_version;
+ rbd_dev->header.image_size = h.image_size;
rbd_dev->header.total_snaps = h.total_snaps;
rbd_dev->header.snapc = h.snapc;
rbd_dev->header.snap_names = h.snap_names;
rbd_dev->header.snap_names_len = h.snap_names_len;
rbd_dev->header.snap_sizes = h.snap_sizes;
- if (follow_seq)
- rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0];
- else
- rbd_dev->header.snapc->seq = snap_seq;
+ /* Free the extra copy of the object prefix */
+ WARN_ON(strcmp(rbd_dev->header.object_prefix, h.object_prefix));
+ kfree(h.object_prefix);
ret = __rbd_init_snaps_header(rbd_dev);
@@ -1733,6 +1749,17 @@ static int __rbd_refresh_header(struct rbd_device *rbd_dev)
return ret;
}
+static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver)
+{
+ int ret;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+ ret = __rbd_refresh_header(rbd_dev, hver);
+ mutex_unlock(&ctl_mutex);
+
+ return ret;
+}
+
static int rbd_init_disk(struct rbd_device *rbd_dev)
{
struct gendisk *disk;
@@ -1762,7 +1789,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
goto out;
snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d",
- rbd_dev->id);
+ rbd_dev->dev_id);
disk->major = rbd_dev->major;
disk->first_minor = 0;
disk->fops = &rbd_bd_ops;
@@ -1819,8 +1846,13 @@ static ssize_t rbd_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
+ sector_t size;
+
+ down_read(&rbd_dev->header_rwsem);
+ size = get_capacity(rbd_dev->disk);
+ up_read(&rbd_dev->header_rwsem);
- return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size);
+ return sprintf(buf, "%llu\n", (unsigned long long) size * SECTOR_SIZE);
}
static ssize_t rbd_major_show(struct device *dev,
@@ -1848,12 +1880,20 @@ static ssize_t rbd_pool_show(struct device *dev,
return sprintf(buf, "%s\n", rbd_dev->pool_name);
}
+static ssize_t rbd_pool_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
+
+ return sprintf(buf, "%d\n", rbd_dev->pool_id);
+}
+
static ssize_t rbd_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- return sprintf(buf, "%s\n", rbd_dev->obj);
+ return sprintf(buf, "%s\n", rbd_dev->image_name);
}
static ssize_t rbd_snap_show(struct device *dev,
@@ -1871,23 +1911,18 @@ static ssize_t rbd_image_refresh(struct device *dev,
size_t size)
{
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- int rc;
- int ret = size;
-
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+ int ret;
- rc = __rbd_refresh_header(rbd_dev);
- if (rc < 0)
- ret = rc;
+ ret = rbd_refresh_header(rbd_dev, NULL);
- mutex_unlock(&ctl_mutex);
- return ret;
+ return ret < 0 ? ret : size;
}
static DEVICE_ATTR(size, S_IRUGO, rbd_size_show, NULL);
static DEVICE_ATTR(major, S_IRUGO, rbd_major_show, NULL);
static DEVICE_ATTR(client_id, S_IRUGO, rbd_client_id_show, NULL);
static DEVICE_ATTR(pool, S_IRUGO, rbd_pool_show, NULL);
+static DEVICE_ATTR(pool_id, S_IRUGO, rbd_pool_id_show, NULL);
static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL);
static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh);
static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL);
@@ -1898,6 +1933,7 @@ static struct attribute *rbd_attrs[] = {
&dev_attr_major.attr,
&dev_attr_client_id.attr,
&dev_attr_pool.attr,
+ &dev_attr_pool_id.attr,
&dev_attr_name.attr,
&dev_attr_current_snap.attr,
&dev_attr_refresh.attr,
@@ -1977,15 +2013,13 @@ static struct device_type rbd_snap_device_type = {
.release = rbd_snap_dev_release,
};
-static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
- struct rbd_snap *snap)
+static void __rbd_remove_snap_dev(struct rbd_snap *snap)
{
list_del(&snap->node);
device_unregister(&snap->dev);
}
-static int rbd_register_snap_dev(struct rbd_device *rbd_dev,
- struct rbd_snap *snap,
+static int rbd_register_snap_dev(struct rbd_snap *snap,
struct device *parent)
{
struct device *dev = &snap->dev;
@@ -2000,29 +2034,36 @@ static int rbd_register_snap_dev(struct rbd_device *rbd_dev,
return ret;
}
-static int __rbd_add_snap_dev(struct rbd_device *rbd_dev,
- int i, const char *name,
- struct rbd_snap **snapp)
+static struct rbd_snap *__rbd_add_snap_dev(struct rbd_device *rbd_dev,
+ int i, const char *name)
{
+ struct rbd_snap *snap;
int ret;
- struct rbd_snap *snap = kzalloc(sizeof(*snap), GFP_KERNEL);
+
+ snap = kzalloc(sizeof (*snap), GFP_KERNEL);
if (!snap)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ ret = -ENOMEM;
snap->name = kstrdup(name, GFP_KERNEL);
+ if (!snap->name)
+ goto err;
+
snap->size = rbd_dev->header.snap_sizes[i];
snap->id = rbd_dev->header.snapc->snaps[i];
if (device_is_registered(&rbd_dev->dev)) {
- ret = rbd_register_snap_dev(rbd_dev, snap,
- &rbd_dev->dev);
+ ret = rbd_register_snap_dev(snap, &rbd_dev->dev);
if (ret < 0)
goto err;
}
- *snapp = snap;
- return 0;
+
+ return snap;
+
err:
kfree(snap->name);
kfree(snap);
- return ret;
+
+ return ERR_PTR(ret);
}
/*
@@ -2055,7 +2096,6 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
const char *name, *first_name;
int i = rbd_dev->header.total_snaps;
struct rbd_snap *snap, *old_snap = NULL;
- int ret;
struct list_head *p, *n;
first_name = rbd_dev->header.snap_names;
@@ -2070,8 +2110,15 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
cur_id = rbd_dev->header.snapc->snaps[i - 1];
if (!i || old_snap->id < cur_id) {
- /* old_snap->id was skipped, thus was removed */
- __rbd_remove_snap_dev(rbd_dev, old_snap);
+ /*
+ * old_snap->id was skipped, thus was
+ * removed. If this rbd_dev is mapped to
+ * the removed snapshot, record that it no
+ * longer exists, to prevent further I/O.
+ */
+ if (rbd_dev->snap_id == old_snap->id)
+ rbd_dev->snap_exists = false;
+ __rbd_remove_snap_dev(old_snap);
continue;
}
if (old_snap->id == cur_id) {
@@ -2091,9 +2138,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
if (cur_id >= old_snap->id)
break;
/* a new snapshot */
- ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap);
- if (ret < 0)
- return ret;
+ snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
+ if (IS_ERR(snap))
+ return PTR_ERR(snap);
/* note that we add it backward so using n and not p */
list_add(&snap->node, n);
@@ -2107,9 +2154,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
WARN_ON(1);
return -EINVAL;
}
- ret = __rbd_add_snap_dev(rbd_dev, i - 1, name, &snap);
- if (ret < 0)
- return ret;
+ snap = __rbd_add_snap_dev(rbd_dev, i - 1, name);
+ if (IS_ERR(snap))
+ return PTR_ERR(snap);
list_add(&snap->node, &rbd_dev->snaps);
}
@@ -2129,14 +2176,13 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
dev->type = &rbd_device_type;
dev->parent = &rbd_root_dev;
dev->release = rbd_dev_release;
- dev_set_name(dev, "%d", rbd_dev->id);
+ dev_set_name(dev, "%d", rbd_dev->dev_id);
ret = device_register(dev);
if (ret < 0)
goto out;
list_for_each_entry(snap, &rbd_dev->snaps, node) {
- ret = rbd_register_snap_dev(rbd_dev, snap,
- &rbd_dev->dev);
+ ret = rbd_register_snap_dev(snap, &rbd_dev->dev);
if (ret < 0)
break;
}
@@ -2155,12 +2201,9 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
int ret, rc;
do {
- ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name,
- rbd_dev->header.obj_version);
+ ret = rbd_req_sync_watch(rbd_dev);
if (ret == -ERANGE) {
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- rc = __rbd_refresh_header(rbd_dev);
- mutex_unlock(&ctl_mutex);
+ rc = rbd_refresh_header(rbd_dev, NULL);
if (rc < 0)
return rc;
}
@@ -2177,7 +2220,7 @@ static atomic64_t rbd_id_max = ATOMIC64_INIT(0);
*/
static void rbd_id_get(struct rbd_device *rbd_dev)
{
- rbd_dev->id = atomic64_inc_return(&rbd_id_max);
+ rbd_dev->dev_id = atomic64_inc_return(&rbd_id_max);
spin_lock(&rbd_dev_list_lock);
list_add_tail(&rbd_dev->node, &rbd_dev_list);
@@ -2191,7 +2234,7 @@ static void rbd_id_get(struct rbd_device *rbd_dev)
static void rbd_id_put(struct rbd_device *rbd_dev)
{
struct list_head *tmp;
- int rbd_id = rbd_dev->id;
+ int rbd_id = rbd_dev->dev_id;
int max_id;
BUG_ON(rbd_id < 1);
@@ -2282,19 +2325,58 @@ static inline size_t copy_token(const char **buf,
}
/*
- * This fills in the pool_name, obj, obj_len, snap_name, obj_len,
+ * Finds the next token in *buf, dynamically allocates a buffer big
+ * enough to hold a copy of it, and copies the token into the new
+ * buffer. The copy is guaranteed to be terminated with '\0'. Note
+ * that a duplicate buffer is created even for a zero-length token.
+ *
+ * Returns a pointer to the newly-allocated duplicate, or a null
+ * pointer if memory for the duplicate was not available. If
+ * the lenp argument is a non-null pointer, the length of the token
+ * (not including the '\0') is returned in *lenp.
+ *
+ * If successful, the *buf pointer will be updated to point beyond
+ * the end of the found token.
+ *
+ * Note: uses GFP_KERNEL for allocation.
+ */
+static inline char *dup_token(const char **buf, size_t *lenp)
+{
+ char *dup;
+ size_t len;
+
+ len = next_token(buf);
+ dup = kmalloc(len + 1, GFP_KERNEL);
+ if (!dup)
+ return NULL;
+
+ memcpy(dup, *buf, len);
+ *(dup + len) = '\0';
+ *buf += len;
+
+ if (lenp)
+ *lenp = len;
+
+ return dup;
+}
+
+/*
+ * This fills in the pool_name, image_name, image_name_len, snap_name,
* rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based
* on the list of monitor addresses and other options provided via
* /sys/bus/rbd/add.
+ *
+ * Note: rbd_dev is assumed to have been initially zero-filled.
*/
static int rbd_add_parse_args(struct rbd_device *rbd_dev,
const char *buf,
const char **mon_addrs,
size_t *mon_addrs_size,
char *options,
- size_t options_size)
+ size_t options_size)
{
- size_t len;
+ size_t len;
+ int ret;
/* The first four tokens are required */
@@ -2310,56 +2392,74 @@ static int rbd_add_parse_args(struct rbd_device *rbd_dev,
if (!len || len >= options_size)
return -EINVAL;
- len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name));
- if (!len || len >= sizeof (rbd_dev->pool_name))
- return -EINVAL;
-
- len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj));
- if (!len || len >= sizeof (rbd_dev->obj))
- return -EINVAL;
+ ret = -ENOMEM;
+ rbd_dev->pool_name = dup_token(&buf, NULL);
+ if (!rbd_dev->pool_name)
+ goto out_err;
- /* We have the object length in hand, save it. */
+ rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len);
+ if (!rbd_dev->image_name)
+ goto out_err;
- rbd_dev->obj_len = len;
+ /* Create the name of the header object */
- BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN
- < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX));
- sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX);
+ rbd_dev->header_name = kmalloc(rbd_dev->image_name_len
+ + sizeof (RBD_SUFFIX),
+ GFP_KERNEL);
+ if (!rbd_dev->header_name)
+ goto out_err;
+ sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX);
/*
- * The snapshot name is optional, but it's an error if it's
- * too long. If no snapshot is supplied, fill in the default.
+ * The snapshot name is optional. If none is is supplied,
+ * we use the default value.
*/
- len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name));
- if (!len)
+ rbd_dev->snap_name = dup_token(&buf, &len);
+ if (!rbd_dev->snap_name)
+ goto out_err;
+ if (!len) {
+ /* Replace the empty name with the default */
+ kfree(rbd_dev->snap_name);
+ rbd_dev->snap_name
+ = kmalloc(sizeof (RBD_SNAP_HEAD_NAME), GFP_KERNEL);
+ if (!rbd_dev->snap_name)
+ goto out_err;
+
memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
sizeof (RBD_SNAP_HEAD_NAME));
- else if (len >= sizeof (rbd_dev->snap_name))
- return -EINVAL;
+ }
return 0;
+
+out_err:
+ kfree(rbd_dev->header_name);
+ kfree(rbd_dev->image_name);
+ kfree(rbd_dev->pool_name);
+ rbd_dev->pool_name = NULL;
+
+ return ret;
}
static ssize_t rbd_add(struct bus_type *bus,
const char *buf,
size_t count)
{
- struct rbd_device *rbd_dev;
+ char *options;
+ struct rbd_device *rbd_dev = NULL;
const char *mon_addrs = NULL;
size_t mon_addrs_size = 0;
- char *options = NULL;
struct ceph_osd_client *osdc;
int rc = -ENOMEM;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
- rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
- if (!rbd_dev)
- goto err_nomem;
options = kmalloc(count, GFP_KERNEL);
if (!options)
goto err_nomem;
+ rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
+ if (!rbd_dev)
+ goto err_nomem;
/* static rbd_device initialization */
spin_lock_init(&rbd_dev->lock);
@@ -2367,15 +2467,13 @@ static ssize_t rbd_add(struct bus_type *bus,
INIT_LIST_HEAD(&rbd_dev->snaps);
init_rwsem(&rbd_dev->header_rwsem);
- init_rwsem(&rbd_dev->header_rwsem);
-
/* generate unique id: find highest unique id, add one */
rbd_id_get(rbd_dev);
/* Fill in the device name, now that we have its id. */
BUILD_BUG_ON(DEV_NAME_LEN
< sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH);
- sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id);
+ sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->dev_id);
/* parse add command */
rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size,
@@ -2395,7 +2493,7 @@ static ssize_t rbd_add(struct bus_type *bus,
rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name);
if (rc < 0)
goto err_out_client;
- rbd_dev->poolid = rc;
+ rbd_dev->pool_id = rc;
/* register our block device */
rc = register_blkdev(0, rbd_dev->name);
@@ -2435,10 +2533,16 @@ err_out_blkdev:
err_out_client:
rbd_put_client(rbd_dev);
err_put_id:
+ if (rbd_dev->pool_name) {
+ kfree(rbd_dev->snap_name);
+ kfree(rbd_dev->header_name);
+ kfree(rbd_dev->image_name);
+ kfree(rbd_dev->pool_name);
+ }
rbd_id_put(rbd_dev);
err_nomem:
- kfree(options);
kfree(rbd_dev);
+ kfree(options);
dout("Error adding device %s\n", buf);
module_put(THIS_MODULE);
@@ -2446,7 +2550,7 @@ err_nomem:
return (ssize_t) rc;
}
-static struct rbd_device *__rbd_get_dev(unsigned long id)
+static struct rbd_device *__rbd_get_dev(unsigned long dev_id)
{
struct list_head *tmp;
struct rbd_device *rbd_dev;
@@ -2454,7 +2558,7 @@ static struct rbd_device *__rbd_get_dev(unsigned long id)
spin_lock(&rbd_dev_list_lock);
list_for_each(tmp, &rbd_dev_list) {
rbd_dev = list_entry(tmp, struct rbd_device, node);
- if (rbd_dev->id == id) {
+ if (rbd_dev->dev_id == dev_id) {
spin_unlock(&rbd_dev_list_lock);
return rbd_dev;
}
@@ -2474,7 +2578,7 @@ static void rbd_dev_release(struct device *dev)
rbd_dev->watch_request);
}
if (rbd_dev->watch_event)
- rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name);
+ rbd_req_sync_unwatch(rbd_dev);
rbd_put_client(rbd_dev);
@@ -2483,6 +2587,10 @@ static void rbd_dev_release(struct device *dev)
unregister_blkdev(rbd_dev->major, rbd_dev->name);
/* done with the id, and with the rbd_dev */
+ kfree(rbd_dev->snap_name);
+ kfree(rbd_dev->header_name);
+ kfree(rbd_dev->pool_name);
+ kfree(rbd_dev->image_name);
rbd_id_put(rbd_dev);
kfree(rbd_dev);
@@ -2544,7 +2652,7 @@ static ssize_t rbd_snap_add(struct device *dev,
if (ret < 0)
goto err_unlock;
- ret = __rbd_refresh_header(rbd_dev);
+ ret = __rbd_refresh_header(rbd_dev, NULL);
if (ret < 0)
goto err_unlock;
@@ -2553,7 +2661,7 @@ static ssize_t rbd_snap_add(struct device *dev,
mutex_unlock(&ctl_mutex);
/* make a best effort, don't error if failed */
- rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name);
+ rbd_req_sync_notify(rbd_dev);
ret = count;
kfree(name);
diff --git a/drivers/block/rbd_types.h b/drivers/block/rbd_types.h
index 950708688f17..0924e9e41a60 100644
--- a/drivers/block/rbd_types.h
+++ b/drivers/block/rbd_types.h
@@ -31,7 +31,6 @@
#define RBD_MIN_OBJ_ORDER 16
#define RBD_MAX_OBJ_ORDER 30
-#define RBD_MAX_OBJ_NAME_LEN 96
#define RBD_MAX_SEG_NAME_LEN 128
#define RBD_COMP_NONE 0
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 9a72277a31df..eb0d8216f557 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -513,42 +513,19 @@ static void process_page(unsigned long data)
}
}
-struct mm_plug_cb {
- struct blk_plug_cb cb;
- struct cardinfo *card;
-};
-
-static void mm_unplug(struct blk_plug_cb *cb)
+static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule)
{
- struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb);
+ struct cardinfo *card = cb->data;
- spin_lock_irq(&mmcb->card->lock);
- activate(mmcb->card);
- spin_unlock_irq(&mmcb->card->lock);
- kfree(mmcb);
+ spin_lock_irq(&card->lock);
+ activate(card);
+ spin_unlock_irq(&card->lock);
+ kfree(cb);
}
static int mm_check_plugged(struct cardinfo *card)
{
- struct blk_plug *plug = current->plug;
- struct mm_plug_cb *mmcb;
-
- if (!plug)
- return 0;
-
- list_for_each_entry(mmcb, &plug->cb_list, cb.list) {
- if (mmcb->cb.callback == mm_unplug && mmcb->card == card)
- return 1;
- }
- /* Not currently on the callback list */
- mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC);
- if (!mmcb)
- return 0;
-
- mmcb->card = card;
- mmcb->cb.callback = mm_unplug;
- list_add(&mmcb->cb.list, &plug->cb_list);
- return 1;
+ return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb));
}
static void mm_make_request(struct request_queue *q, struct bio *bio)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 693187df7601..c0bbeb470754 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -21,8 +21,6 @@ struct workqueue_struct *virtblk_wq;
struct virtio_blk
{
- spinlock_t lock;
-
struct virtio_device *vdev;
struct virtqueue *vq;
@@ -65,7 +63,7 @@ static void blk_done(struct virtqueue *vq)
unsigned int len;
unsigned long flags;
- spin_lock_irqsave(&vblk->lock, flags);
+ spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
int error;
@@ -99,7 +97,7 @@ static void blk_done(struct virtqueue *vq)
}
/* In case queue is stopped waiting for more buffers. */
blk_start_queue(vblk->disk->queue);
- spin_unlock_irqrestore(&vblk->lock, flags);
+ spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags);
}
static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
@@ -397,6 +395,83 @@ static int virtblk_name_format(char *prefix, int index, char *buf, int buflen)
return 0;
}
+static int virtblk_get_cache_mode(struct virtio_device *vdev)
+{
+ u8 writeback;
+ int err;
+
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_CONFIG_WCE,
+ offsetof(struct virtio_blk_config, wce),
+ &writeback);
+ if (err)
+ writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE);
+
+ return writeback;
+}
+
+static void virtblk_update_cache_mode(struct virtio_device *vdev)
+{
+ u8 writeback = virtblk_get_cache_mode(vdev);
+ struct virtio_blk *vblk = vdev->priv;
+
+ if (writeback)
+ blk_queue_flush(vblk->disk->queue, REQ_FLUSH);
+ else
+ blk_queue_flush(vblk->disk->queue, 0);
+
+ revalidate_disk(vblk->disk);
+}
+
+static const char *const virtblk_cache_types[] = {
+ "write through", "write back"
+};
+
+static ssize_t
+virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+ struct virtio_blk *vblk = disk->private_data;
+ struct virtio_device *vdev = vblk->vdev;
+ int i;
+ u8 writeback;
+
+ BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));
+ for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; )
+ if (sysfs_streq(buf, virtblk_cache_types[i]))
+ break;
+
+ if (i < 0)
+ return -EINVAL;
+
+ writeback = i;
+ vdev->config->set(vdev,
+ offsetof(struct virtio_blk_config, wce),
+ &writeback, sizeof(writeback));
+
+ virtblk_update_cache_mode(vdev);
+ return count;
+}
+
+static ssize_t
+virtblk_cache_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+ struct virtio_blk *vblk = disk->private_data;
+ u8 writeback = virtblk_get_cache_mode(vblk->vdev);
+
+ BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types));
+ return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]);
+}
+
+static const struct device_attribute dev_attr_cache_type_ro =
+ __ATTR(cache_type, S_IRUGO,
+ virtblk_cache_type_show, NULL);
+static const struct device_attribute dev_attr_cache_type_rw =
+ __ATTR(cache_type, S_IRUGO|S_IWUSR,
+ virtblk_cache_type_show, virtblk_cache_type_store);
+
static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
@@ -431,7 +506,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
goto out_free_index;
}
- spin_lock_init(&vblk->lock);
vblk->vdev = vdev;
vblk->sg_elems = sg_elems;
sg_init_table(vblk->sg, vblk->sg_elems);
@@ -456,7 +530,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
goto out_mempool;
}
- q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock);
+ q = vblk->disk->queue = blk_init_queue(do_virtblk_request, NULL);
if (!q) {
err = -ENOMEM;
goto out_put_disk;
@@ -474,8 +548,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
vblk->index = index;
/* configure queue flush support */
- if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
- blk_queue_flush(q, REQ_FLUSH);
+ virtblk_update_cache_mode(vdev);
/* If disk is read-only in the host, the guest should obey */
if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
@@ -553,6 +626,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
if (err)
goto out_del_disk;
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE))
+ err = device_create_file(disk_to_dev(vblk->disk),
+ &dev_attr_cache_type_rw);
+ else
+ err = device_create_file(disk_to_dev(vblk->disk),
+ &dev_attr_cache_type_ro);
+ if (err)
+ goto out_del_disk;
return 0;
out_del_disk:
@@ -576,30 +657,20 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
int index = vblk->index;
- struct virtblk_req *vbr;
- unsigned long flags;
/* Prevent config work handler from accessing the device. */
mutex_lock(&vblk->config_lock);
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
+ del_gendisk(vblk->disk);
+ blk_cleanup_queue(vblk->disk->queue);
+
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
flush_work(&vblk->config_work);
- del_gendisk(vblk->disk);
-
- /* Abort requests dispatched to driver. */
- spin_lock_irqsave(&vblk->lock, flags);
- while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) {
- __blk_end_request_all(vbr->req, -EIO);
- mempool_free(vbr, vblk->pool);
- }
- spin_unlock_irqrestore(&vblk->lock, flags);
-
- blk_cleanup_queue(vblk->disk->queue);
put_disk(vblk->disk);
mempool_destroy(vblk->pool);
vdev->config->del_vqs(vdev);
@@ -655,7 +726,7 @@ static const struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY,
VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_SCSI,
- VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY
+ VIRTIO_BLK_F_WCE, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE
};
/*
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index e4fb3374dcd2..2c2d2e5c1597 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -888,9 +888,8 @@ static int setup_blkring(struct xenbus_device *dev,
if (err)
goto fail;
- err = bind_evtchn_to_irqhandler(info->evtchn,
- blkif_interrupt,
- IRQF_SAMPLE_RANDOM, "blkif", info);
+ err = bind_evtchn_to_irqhandler(info->evtchn, blkif_interrupt, 0,
+ "blkif", info);
if (err <= 0) {
xenbus_dev_fatal(dev, err,
"bind_evtchn_to_irqhandler failed");
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 10308cd8a7ed..fc2de5528dcc 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -79,12 +79,14 @@ static struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0930, 0x0219) },
+ { USB_DEVICE(0x0489, 0xe057) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C) },
+ { USB_DEVICE(0x0489, 0xE036) },
{ } /* Terminating entry */
};
@@ -104,9 +106,11 @@ static struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
{ } /* Terminating entry */
};
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index f637c2550016..e5921d681ddb 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -52,6 +52,9 @@ static struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+ /* Apple-specific (Broadcom) devices */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+
/* Broadcom SoftSailing reporting vendor specific */
{ USB_DEVICE(0x0a5c, 0x21e1) },
@@ -94,15 +97,14 @@ static struct usb_device_id btusb_table[] = {
/* Broadcom BCM20702A0 */
{ USB_DEVICE(0x0489, 0xe042) },
- { USB_DEVICE(0x0a5c, 0x21e3) },
- { USB_DEVICE(0x0a5c, 0x21e6) },
- { USB_DEVICE(0x0a5c, 0x21e8) },
- { USB_DEVICE(0x0a5c, 0x21f3) },
{ USB_DEVICE(0x413c, 0x8197) },
/* Foxconn - Hon Hai */
{ USB_DEVICE(0x0489, 0xe033) },
+ /*Broadcom devices with vendor specific id */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+
{ } /* Terminating entry */
};
@@ -133,12 +135,14 @@ static struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index b01d67328243..7c0d391996b5 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -73,6 +73,20 @@ config HW_RANDOM_ATMEL
If unsure, say Y.
+config HW_RANDOM_BCM63XX
+ tristate "Broadcom BCM63xx Random Number Generator support"
+ depends on HW_RANDOM && BCM63XX
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on the Broadcom BCM63xx SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm63xx-rng
+
+ If unusure, say Y.
+
+
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 8d6d173b65e6..39a757ca15b6 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM63XX) += bcm63xx-rng.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
n2-rng-y := n2-drv.o n2-asm.o
diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c
new file mode 100644
index 000000000000..aec6a4277caa
--- /dev/null
+++ b/drivers/char/hw_random/bcm63xx-rng.c
@@ -0,0 +1,175 @@
+/*
+ * Broadcom BCM63xx Random Number Generator support
+ *
+ * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2009, Broadcom Corporation
+ *
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+struct bcm63xx_rng_priv {
+ struct clk *clk;
+ void __iomem *regs;
+};
+
+#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv)
+
+static int bcm63xx_rng_init(struct hwrng *rng)
+{
+ struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+ u32 val;
+
+ val = bcm_readl(priv->regs + RNG_CTRL);
+ val |= RNG_EN;
+ bcm_writel(val, priv->regs + RNG_CTRL);
+
+ return 0;
+}
+
+static void bcm63xx_rng_cleanup(struct hwrng *rng)
+{
+ struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+ u32 val;
+
+ val = bcm_readl(priv->regs + RNG_CTRL);
+ val &= ~RNG_EN;
+ bcm_writel(val, priv->regs + RNG_CTRL);
+}
+
+static int bcm63xx_rng_data_present(struct hwrng *rng, int wait)
+{
+ struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+ return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK;
+}
+
+static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+ *data = bcm_readl(priv->regs + RNG_DATA);
+
+ return 4;
+}
+
+static int __devinit bcm63xx_rng_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct clk *clk;
+ int ret;
+ struct bcm63xx_rng_priv *priv;
+ struct hwrng *rng;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no iomem resource\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "no memory for private structure\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ rng = kzalloc(sizeof(*rng), GFP_KERNEL);
+ if (!rng) {
+ dev_err(&pdev->dev, "no memory for rng structure\n");
+ ret = -ENOMEM;
+ goto out_free_priv;
+ }
+
+ platform_set_drvdata(pdev, rng);
+ rng->priv = (unsigned long)priv;
+ rng->name = pdev->name;
+ rng->init = bcm63xx_rng_init;
+ rng->cleanup = bcm63xx_rng_cleanup;
+ rng->data_present = bcm63xx_rng_data_present;
+ rng->data_read = bcm63xx_rng_data_read;
+
+ clk = clk_get(&pdev->dev, "ipsec");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "no clock for device\n");
+ ret = PTR_ERR(clk);
+ goto out_free_rng;
+ }
+
+ priv->clk = clk;
+
+ if (!devm_request_mem_region(&pdev->dev, r->start,
+ resource_size(r), pdev->name)) {
+ dev_err(&pdev->dev, "request mem failed");
+ ret = -ENOMEM;
+ goto out_free_rng;
+ }
+
+ priv->regs = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
+ if (!priv->regs) {
+ dev_err(&pdev->dev, "ioremap failed");
+ ret = -ENOMEM;
+ goto out_free_rng;
+ }
+
+ clk_enable(clk);
+
+ ret = hwrng_register(rng);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register rng device\n");
+ goto out_clk_disable;
+ }
+
+ dev_info(&pdev->dev, "registered RNG driver\n");
+
+ return 0;
+
+out_clk_disable:
+ clk_disable(clk);
+out_free_rng:
+ platform_set_drvdata(pdev, NULL);
+ kfree(rng);
+out_free_priv:
+ kfree(priv);
+out:
+ return ret;
+}
+
+static int __devexit bcm63xx_rng_remove(struct platform_device *pdev)
+{
+ struct hwrng *rng = platform_get_drvdata(pdev);
+ struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
+
+ hwrng_unregister(rng);
+ clk_disable(priv->clk);
+ kfree(priv);
+ kfree(rng);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver bcm63xx_rng_driver = {
+ .probe = bcm63xx_rng_probe,
+ .remove = __devexit_p(bcm63xx_rng_remove),
+ .driver = {
+ .name = "bcm63xx-rng",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(bcm63xx_rng_driver);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 723725bbb96b..5708299507d0 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -55,6 +55,7 @@ static void register_buffer(u8 *buf, size_t size)
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
+ int ret;
if (!busy) {
busy = true;
@@ -65,7 +66,9 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
if (!wait)
return 0;
- wait_for_completion(&have_data);
+ ret = wait_for_completion_killable(&have_data);
+ if (ret < 0)
+ return ret;
busy = false;
@@ -85,7 +88,7 @@ static struct hwrng virtio_hwrng = {
.read = virtio_read,
};
-static int virtrng_probe(struct virtio_device *vdev)
+static int probe_common(struct virtio_device *vdev)
{
int err;
@@ -103,13 +106,37 @@ static int virtrng_probe(struct virtio_device *vdev)
return 0;
}
-static void __devexit virtrng_remove(struct virtio_device *vdev)
+static void remove_common(struct virtio_device *vdev)
{
vdev->config->reset(vdev);
+ busy = false;
hwrng_unregister(&virtio_hwrng);
vdev->config->del_vqs(vdev);
}
+static int virtrng_probe(struct virtio_device *vdev)
+{
+ return probe_common(vdev);
+}
+
+static void __devexit virtrng_remove(struct virtio_device *vdev)
+{
+ remove_common(vdev);
+}
+
+#ifdef CONFIG_PM
+static int virtrng_freeze(struct virtio_device *vdev)
+{
+ remove_common(vdev);
+ return 0;
+}
+
+static int virtrng_restore(struct virtio_device *vdev)
+{
+ return probe_common(vdev);
+}
+#endif
+
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
{ 0 },
@@ -121,6 +148,10 @@ static struct virtio_driver virtio_rng_driver = {
.id_table = id_table,
.probe = virtrng_probe,
.remove = __devexit_p(virtrng_remove),
+#ifdef CONFIG_PM
+ .freeze = virtrng_freeze,
+ .restore = virtrng_restore,
+#endif
};
static int __init init(void)
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 8b78750f1efe..845f97fd1832 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -283,7 +283,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
vdata->flags = flags;
vdata->type = type;
spin_lock_init(&vdata->lock);
- vdata->refcnt = ATOMIC_INIT(1);
+ atomic_set(&vdata->refcnt, 1);
vma->vm_private_data = vdata;
vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 4ec04a754733..b86eae9b77df 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -125,21 +125,26 @@
* The current exported interfaces for gathering environmental noise
* from the devices are:
*
+ * void add_device_randomness(const void *buf, unsigned int size);
* void add_input_randomness(unsigned int type, unsigned int code,
* unsigned int value);
- * void add_interrupt_randomness(int irq);
+ * void add_interrupt_randomness(int irq, int irq_flags);
* void add_disk_randomness(struct gendisk *disk);
*
+ * add_device_randomness() is for adding data to the random pool that
+ * is likely to differ between two devices (or possibly even per boot).
+ * This would be things like MAC addresses or serial numbers, or the
+ * read-out of the RTC. This does *not* add any actual entropy to the
+ * pool, but it initializes the pool to different values for devices
+ * that might otherwise be identical and have very little entropy
+ * available to them (particularly common in the embedded world).
+ *
* add_input_randomness() uses the input layer interrupt timing, as well as
* the event type information from the hardware.
*
- * add_interrupt_randomness() uses the inter-interrupt timing as random
- * inputs to the entropy pool. Note that not all interrupts are good
- * sources of randomness! For example, the timer interrupts is not a
- * good choice, because the periodicity of the interrupts is too
- * regular, and hence predictable to an attacker. Network Interface
- * Controller interrupts are a better measure, since the timing of the
- * NIC interrupts are more unpredictable.
+ * add_interrupt_randomness() uses the interrupt timing as random
+ * inputs to the entropy pool. Using the cycle counters and the irq source
+ * as inputs, it feeds the randomness roughly once a second.
*
* add_disk_randomness() uses what amounts to the seek time of block
* layer request events, on a per-disk_devt basis, as input to the
@@ -248,6 +253,8 @@
#include <linux/percpu.h>
#include <linux/cryptohash.h>
#include <linux/fips.h>
+#include <linux/ptrace.h>
+#include <linux/kmemcheck.h>
#ifdef CONFIG_GENERIC_HARDIRQS
# include <linux/irq.h>
@@ -256,8 +263,12 @@
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
+#include <asm/irq_regs.h>
#include <asm/io.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/random.h>
+
/*
* Configuration information
*/
@@ -266,6 +277,8 @@
#define SEC_XFER_SIZE 512
#define EXTRACT_SIZE 10
+#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
+
/*
* The minimum number of bits of entropy before we wake up a read on
* /dev/random. Should be enough to do a significant reseed.
@@ -420,8 +433,10 @@ struct entropy_store {
/* read-write data: */
spinlock_t lock;
unsigned add_ptr;
+ unsigned input_rotate;
int entropy_count;
- int input_rotate;
+ int entropy_total;
+ unsigned int initialized:1;
__u8 last_data[EXTRACT_SIZE];
};
@@ -454,6 +469,10 @@ static struct entropy_store nonblocking_pool = {
.pool = nonblocking_pool_data
};
+static __u32 const twist_table[8] = {
+ 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+
/*
* This function adds bytes into the entropy "pool". It does not
* update the entropy estimate. The caller should call
@@ -464,29 +483,24 @@ static struct entropy_store nonblocking_pool = {
* it's cheap to do so and helps slightly in the expected case where
* the entropy is concentrated in the low-order bits.
*/
-static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
- int nbytes, __u8 out[64])
+static void _mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
{
- static __u32 const twist_table[8] = {
- 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
- 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
unsigned long i, j, tap1, tap2, tap3, tap4, tap5;
int input_rotate;
int wordmask = r->poolinfo->poolwords - 1;
const char *bytes = in;
__u32 w;
- unsigned long flags;
- /* Taps are constant, so we can load them without holding r->lock. */
tap1 = r->poolinfo->tap1;
tap2 = r->poolinfo->tap2;
tap3 = r->poolinfo->tap3;
tap4 = r->poolinfo->tap4;
tap5 = r->poolinfo->tap5;
- spin_lock_irqsave(&r->lock, flags);
- input_rotate = r->input_rotate;
- i = r->add_ptr;
+ smp_rmb();
+ input_rotate = ACCESS_ONCE(r->input_rotate);
+ i = ACCESS_ONCE(r->add_ptr);
/* mix one byte at a time to simplify size handling and churn faster */
while (nbytes--) {
@@ -513,19 +527,61 @@ static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
input_rotate += i ? 7 : 14;
}
- r->input_rotate = input_rotate;
- r->add_ptr = i;
+ ACCESS_ONCE(r->input_rotate) = input_rotate;
+ ACCESS_ONCE(r->add_ptr) = i;
+ smp_wmb();
if (out)
for (j = 0; j < 16; j++)
((__u32 *)out)[j] = r->pool[(i - j) & wordmask];
+}
+
+static void __mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
+{
+ trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_);
+ _mix_pool_bytes(r, in, nbytes, out);
+}
+
+static void mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
+{
+ unsigned long flags;
+ trace_mix_pool_bytes(r->name, nbytes, _RET_IP_);
+ spin_lock_irqsave(&r->lock, flags);
+ _mix_pool_bytes(r, in, nbytes, out);
spin_unlock_irqrestore(&r->lock, flags);
}
-static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
+struct fast_pool {
+ __u32 pool[4];
+ unsigned long last;
+ unsigned short count;
+ unsigned char rotate;
+ unsigned char last_timer_intr;
+};
+
+/*
+ * This is a fast mixing routine used by the interrupt randomness
+ * collector. It's hardcoded for an 128 bit pool and assumes that any
+ * locks that might be needed are taken by the caller.
+ */
+static void fast_mix(struct fast_pool *f, const void *in, int nbytes)
{
- mix_pool_bytes_extract(r, in, bytes, NULL);
+ const char *bytes = in;
+ __u32 w;
+ unsigned i = f->count;
+ unsigned input_rotate = f->rotate;
+
+ while (nbytes--) {
+ w = rol32(*bytes++, input_rotate & 31) ^ f->pool[i & 3] ^
+ f->pool[(i + 1) & 3];
+ f->pool[i & 3] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate += (i++ & 3) ? 7 : 14;
+ }
+ f->count = i;
+ f->rotate = input_rotate;
}
/*
@@ -533,30 +589,38 @@ static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
*/
static void credit_entropy_bits(struct entropy_store *r, int nbits)
{
- unsigned long flags;
- int entropy_count;
+ int entropy_count, orig;
if (!nbits)
return;
- spin_lock_irqsave(&r->lock, flags);
-
DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
- entropy_count = r->entropy_count;
+retry:
+ entropy_count = orig = ACCESS_ONCE(r->entropy_count);
entropy_count += nbits;
+
if (entropy_count < 0) {
DEBUG_ENT("negative entropy/overflow\n");
entropy_count = 0;
} else if (entropy_count > r->poolinfo->POOLBITS)
entropy_count = r->poolinfo->POOLBITS;
- r->entropy_count = entropy_count;
+ if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+ goto retry;
+
+ if (!r->initialized && nbits > 0) {
+ r->entropy_total += nbits;
+ if (r->entropy_total > 128)
+ r->initialized = 1;
+ }
+
+ trace_credit_entropy_bits(r->name, nbits, entropy_count,
+ r->entropy_total, _RET_IP_);
/* should we wake readers? */
if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) {
wake_up_interruptible(&random_read_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
}
- spin_unlock_irqrestore(&r->lock, flags);
}
/*********************************************************************
@@ -572,42 +636,24 @@ struct timer_rand_state {
unsigned dont_count_entropy:1;
};
-#ifndef CONFIG_GENERIC_HARDIRQS
-
-static struct timer_rand_state *irq_timer_state[NR_IRQS];
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
- return irq_timer_state[irq];
-}
-
-static void set_timer_rand_state(unsigned int irq,
- struct timer_rand_state *state)
-{
- irq_timer_state[irq] = state;
-}
-
-#else
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
- struct irq_desc *desc;
-
- desc = irq_to_desc(irq);
-
- return desc->timer_rand_state;
-}
-
-static void set_timer_rand_state(unsigned int irq,
- struct timer_rand_state *state)
+/*
+ * Add device- or boot-specific data to the input and nonblocking
+ * pools to help initialize them to unique values.
+ *
+ * None of this adds any entropy, it is meant to avoid the
+ * problem of the nonblocking pool having similar initial state
+ * across largely identical devices.
+ */
+void add_device_randomness(const void *buf, unsigned int size)
{
- struct irq_desc *desc;
+ unsigned long time = get_cycles() ^ jiffies;
- desc = irq_to_desc(irq);
-
- desc->timer_rand_state = state;
+ mix_pool_bytes(&input_pool, buf, size, NULL);
+ mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
+ mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
+ mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
}
-#endif
+EXPORT_SYMBOL(add_device_randomness);
static struct timer_rand_state input_timer_state;
@@ -637,13 +683,9 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
goto out;
sample.jiffies = jiffies;
-
- /* Use arch random value, fall back to cycles */
- if (!arch_get_random_int(&sample.cycles))
- sample.cycles = get_cycles();
-
+ sample.cycles = get_cycles();
sample.num = num;
- mix_pool_bytes(&input_pool, &sample, sizeof(sample));
+ mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
/*
* Calculate number of bits of randomness we probably added.
@@ -700,17 +742,48 @@ void add_input_randomness(unsigned int type, unsigned int code,
}
EXPORT_SYMBOL_GPL(add_input_randomness);
-void add_interrupt_randomness(int irq)
+static DEFINE_PER_CPU(struct fast_pool, irq_randomness);
+
+void add_interrupt_randomness(int irq, int irq_flags)
{
- struct timer_rand_state *state;
+ struct entropy_store *r;
+ struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness);
+ struct pt_regs *regs = get_irq_regs();
+ unsigned long now = jiffies;
+ __u32 input[4], cycles = get_cycles();
+
+ input[0] = cycles ^ jiffies;
+ input[1] = irq;
+ if (regs) {
+ __u64 ip = instruction_pointer(regs);
+ input[2] = ip;
+ input[3] = ip >> 32;
+ }
- state = get_timer_rand_state(irq);
+ fast_mix(fast_pool, input, sizeof(input));
- if (state == NULL)
+ if ((fast_pool->count & 1023) &&
+ !time_after(now, fast_pool->last + HZ))
return;
- DEBUG_ENT("irq event %d\n", irq);
- add_timer_randomness(state, 0x100 + irq);
+ fast_pool->last = now;
+
+ r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+ __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+ /*
+ * If we don't have a valid cycle counter, and we see
+ * back-to-back timer interrupts, then skip giving credit for
+ * any entropy.
+ */
+ if (cycles == 0) {
+ if (irq_flags & __IRQF_TIMER) {
+ if (fast_pool->last_timer_intr)
+ return;
+ fast_pool->last_timer_intr = 1;
+ } else
+ fast_pool->last_timer_intr = 0;
+ }
+ credit_entropy_bits(r, 1);
}
#ifdef CONFIG_BLOCK
@@ -742,7 +815,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
*/
static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
{
- __u32 tmp[OUTPUT_POOL_WORDS];
+ __u32 tmp[OUTPUT_POOL_WORDS];
if (r->pull && r->entropy_count < nbytes * 8 &&
r->entropy_count < r->poolinfo->POOLBITS) {
@@ -761,7 +834,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
bytes = extract_entropy(r->pull, tmp, bytes,
random_read_wakeup_thresh / 8, rsvd);
- mix_pool_bytes(r, tmp, bytes);
+ mix_pool_bytes(r, tmp, bytes, NULL);
credit_entropy_bits(r, bytes*8);
}
}
@@ -820,13 +893,19 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
static void extract_buf(struct entropy_store *r, __u8 *out)
{
int i;
- __u32 hash[5], workspace[SHA_WORKSPACE_WORDS];
+ union {
+ __u32 w[5];
+ unsigned long l[LONGS(EXTRACT_SIZE)];
+ } hash;
+ __u32 workspace[SHA_WORKSPACE_WORDS];
__u8 extract[64];
+ unsigned long flags;
/* Generate a hash across the pool, 16 words (512 bits) at a time */
- sha_init(hash);
+ sha_init(hash.w);
+ spin_lock_irqsave(&r->lock, flags);
for (i = 0; i < r->poolinfo->poolwords; i += 16)
- sha_transform(hash, (__u8 *)(r->pool + i), workspace);
+ sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
/*
* We mix the hash back into the pool to prevent backtracking
@@ -837,13 +916,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
* brute-forcing the feedback as hard as brute-forcing the
* hash.
*/
- mix_pool_bytes_extract(r, hash, sizeof(hash), extract);
+ __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract);
+ spin_unlock_irqrestore(&r->lock, flags);
/*
* To avoid duplicates, we atomically extract a portion of the
* pool while mixing, and hash one final time.
*/
- sha_transform(hash, extract, workspace);
+ sha_transform(hash.w, extract, workspace);
memset(extract, 0, sizeof(extract));
memset(workspace, 0, sizeof(workspace));
@@ -852,20 +932,32 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
* pattern, we fold it in half. Thus, we always feed back
* twice as much data as we output.
*/
- hash[0] ^= hash[3];
- hash[1] ^= hash[4];
- hash[2] ^= rol32(hash[2], 16);
- memcpy(out, hash, EXTRACT_SIZE);
- memset(hash, 0, sizeof(hash));
+ hash.w[0] ^= hash.w[3];
+ hash.w[1] ^= hash.w[4];
+ hash.w[2] ^= rol32(hash.w[2], 16);
+
+ /*
+ * If we have a architectural hardware random number
+ * generator, mix that in, too.
+ */
+ for (i = 0; i < LONGS(EXTRACT_SIZE); i++) {
+ unsigned long v;
+ if (!arch_get_random_long(&v))
+ break;
+ hash.l[i] ^= v;
+ }
+
+ memcpy(out, &hash, EXTRACT_SIZE);
+ memset(&hash, 0, sizeof(hash));
}
static ssize_t extract_entropy(struct entropy_store *r, void *buf,
- size_t nbytes, int min, int reserved)
+ size_t nbytes, int min, int reserved)
{
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
- unsigned long flags;
+ trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, min, reserved);
@@ -873,6 +965,8 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
extract_buf(r, tmp);
if (fips_enabled) {
+ unsigned long flags;
+
spin_lock_irqsave(&r->lock, flags);
if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
panic("Hardware RNG duplicated output!\n");
@@ -898,6 +992,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
+ trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, 0, 0);
@@ -931,17 +1026,35 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
/*
* This function is the exported kernel interface. It returns some
- * number of good random numbers, suitable for seeding TCP sequence
- * numbers, etc.
+ * number of good random numbers, suitable for key generation, seeding
+ * TCP sequence numbers, etc. It does not use the hw random number
+ * generator, if available; use get_random_bytes_arch() for that.
*/
void get_random_bytes(void *buf, int nbytes)
{
+ extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
+}
+EXPORT_SYMBOL(get_random_bytes);
+
+/*
+ * This function will use the architecture-specific hardware random
+ * number generator if it is available. The arch-specific hw RNG will
+ * almost certainly be faster than what we can do in software, but it
+ * is impossible to verify that it is implemented securely (as
+ * opposed, to, say, the AES encryption of a sequence number using a
+ * key known by the NSA). So it's useful if we need the speed, but
+ * only if we're willing to trust the hardware manufacturer not to
+ * have put in a back door.
+ */
+void get_random_bytes_arch(void *buf, int nbytes)
+{
char *p = buf;
+ trace_get_random_bytes(nbytes, _RET_IP_);
while (nbytes) {
unsigned long v;
int chunk = min(nbytes, (int)sizeof(unsigned long));
-
+
if (!arch_get_random_long(&v))
break;
@@ -950,9 +1063,11 @@ void get_random_bytes(void *buf, int nbytes)
nbytes -= chunk;
}
- extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
+ if (nbytes)
+ extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
}
-EXPORT_SYMBOL(get_random_bytes);
+EXPORT_SYMBOL(get_random_bytes_arch);
+
/*
* init_std_data - initialize pool with system data
@@ -966,23 +1081,30 @@ EXPORT_SYMBOL(get_random_bytes);
static void init_std_data(struct entropy_store *r)
{
int i;
- ktime_t now;
- unsigned long flags;
+ ktime_t now = ktime_get_real();
+ unsigned long rv;
- spin_lock_irqsave(&r->lock, flags);
r->entropy_count = 0;
- spin_unlock_irqrestore(&r->lock, flags);
-
- now = ktime_get_real();
- mix_pool_bytes(r, &now, sizeof(now));
- for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof flags) {
- if (!arch_get_random_long(&flags))
+ r->entropy_total = 0;
+ mix_pool_bytes(r, &now, sizeof(now), NULL);
+ for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) {
+ if (!arch_get_random_long(&rv))
break;
- mix_pool_bytes(r, &flags, sizeof(flags));
+ mix_pool_bytes(r, &rv, sizeof(rv), NULL);
}
- mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
+ mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL);
}
+/*
+ * Note that setup_arch() may call add_device_randomness()
+ * long before we get here. This allows seeding of the pools
+ * with some platform dependent data very early in the boot
+ * process. But it limits our options here. We must use
+ * statically allocated structures that already have all
+ * initializations complete at compile time. We should also
+ * take care not to overwrite the precious per platform data
+ * we were given.
+ */
static int rand_initialize(void)
{
init_std_data(&input_pool);
@@ -992,24 +1114,6 @@ static int rand_initialize(void)
}
module_init(rand_initialize);
-void rand_initialize_irq(int irq)
-{
- struct timer_rand_state *state;
-
- state = get_timer_rand_state(irq);
-
- if (state)
- return;
-
- /*
- * If kzalloc returns null, we just won't use that entropy
- * source.
- */
- state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state)
- set_timer_rand_state(irq, state);
-}
-
#ifdef CONFIG_BLOCK
void rand_initialize_disk(struct gendisk *disk)
{
@@ -1117,7 +1221,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count)
count -= bytes;
p += bytes;
- mix_pool_bytes(r, buf, bytes);
+ mix_pool_bytes(r, buf, bytes, NULL);
cond_resched();
}
@@ -1279,6 +1383,7 @@ static int proc_do_uuid(ctl_table *table, int write,
}
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
+extern ctl_table random_table[];
ctl_table random_table[] = {
{
.procname = "poolsize",
@@ -1344,7 +1449,7 @@ late_initcall(random_int_secret_init);
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
-DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
+static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
unsigned int get_random_int(void)
{
__u32 *hash;
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3f99b9099658..7f0b5ca78516 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -25,7 +25,6 @@ menu "Common Clock Framework"
config COMMON_CLK_DEBUG
bool "DebugFS representation of clock tree"
- depends on COMMON_CLK
select DEBUG_FS
---help---
Creates a directory hierchy in debugfs for visualizing the clk
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c87fdd710560..efdfd009c270 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -465,6 +465,9 @@ static void __clk_disable(struct clk *clk)
if (!clk)
return;
+ if (WARN_ON(IS_ERR(clk)))
+ return;
+
if (WARN_ON(clk->enable_count == 0))
return;
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
index a88331644ebf..e64c253cb169 100644
--- a/drivers/cpufreq/exynos5250-cpufreq.c
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -65,20 +65,20 @@ static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
* Clock divider value for following
* { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
*/
- { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */
- { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */
- { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */
- { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */
- { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */
- { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */
- { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */
- { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */
- { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */
- { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */
+ { 0, 3, 7, 7, 7, 3, 5, 0 }, /* 1700 MHz */
+ { 0, 3, 7, 7, 7, 1, 4, 0 }, /* 1600 MHz */
+ { 0, 2, 7, 7, 7, 1, 4, 0 }, /* 1500 MHz */
+ { 0, 2, 7, 7, 6, 1, 4, 0 }, /* 1400 MHz */
+ { 0, 2, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */
+ { 0, 2, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */
+ { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1100 MHz */
+ { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */
+ { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */
+ { 0, 1, 7, 7, 4, 1, 2, 0 }, /* 800 MHz */
{ 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */
- { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */
+ { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 600 MHz */
{ 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */
- { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */
+ { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 400 MHz */
{ 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */
{ 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */
};
@@ -87,9 +87,9 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
/* Clock divider value for following
* { COPY, HPM }
*/
- { 0, 2 }, /* 1700 MHz - N/A */
- { 0, 2 }, /* 1600 MHz - N/A */
- { 0, 2 }, /* 1500 MHz - N/A */
+ { 0, 2 }, /* 1700 MHz */
+ { 0, 2 }, /* 1600 MHz */
+ { 0, 2 }, /* 1500 MHz */
{ 0, 2 }, /* 1400 MHz */
{ 0, 2 }, /* 1300 MHz */
{ 0, 2 }, /* 1200 MHz */
@@ -106,10 +106,10 @@ static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
};
static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
- (0), /* 1700 MHz - N/A */
- (0), /* 1600 MHz - N/A */
- (0), /* 1500 MHz - N/A */
- (0), /* 1400 MHz */
+ ((425 << 16) | (6 << 8) | 0), /* 1700 MHz */
+ ((200 << 16) | (3 << 8) | 0), /* 1600 MHz */
+ ((250 << 16) | (4 << 8) | 0), /* 1500 MHz */
+ ((175 << 16) | (3 << 8) | 0), /* 1400 MHz */
((325 << 16) | (6 << 8) | 0), /* 1300 MHz */
((200 << 16) | (4 << 8) | 0), /* 1200 MHz */
((275 << 16) | (6 << 8) | 0), /* 1100 MHz */
@@ -126,9 +126,10 @@ static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
/* ASV group voltage table */
static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
- 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */
- 1175000, 1125000, 1075000, 1050000, 1000000,
- 950000, 925000, 925000, 900000
+ 1300000, 1250000, 1225000, 1200000, 1150000,
+ 1125000, 1100000, 1075000, 1050000, 1025000,
+ 1012500, 1000000, 975000, 950000, 937500,
+ 925000
};
static void set_clkdiv(unsigned int div_index)
@@ -248,15 +249,7 @@ static void __init set_volt_table(void)
{
unsigned int i;
- exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
- exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
-
- max_support_idx = L7;
+ max_support_idx = L0;
for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
exynos5250_volt_table[i] = asv_voltage_5250[i];
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 67b97c5fd859..a8bd0310f8fe 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1610,8 +1610,7 @@ static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip,
sprintf(p->irq_name, "%s-%d", irq_name, index);
- return request_irq(p->irq, handler, IRQF_SAMPLE_RANDOM,
- p->irq_name, p);
+ return request_irq(p->irq, handler, 0, p->irq_name, p);
}
static struct kmem_cache *queue_cache[2];
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index d45cf1bcbde5..d06ea2950dd9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -53,6 +53,7 @@ config AMBA_PL08X
bool "ARM PrimeCell PL080 or PL081 support"
depends on ARM_AMBA && EXPERIMENTAL
select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
help
Platform has a PL08x DMAC device
which can provide DMA engine support
@@ -269,6 +270,7 @@ config DMA_SA11X0
tristate "SA-11x0 DMA support"
depends on ARCH_SA1100
select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
help
Support the DMA engine found on Intel StrongARM SA-1100 and
SA-1110 SoCs. This DMA engine can only be used with on-chip
@@ -284,9 +286,18 @@ config MMP_TDMA
Say Y here if you enabled MMP ADMA, otherwise say N.
+config DMA_OMAP
+ tristate "OMAP DMA support"
+ depends on ARCH_OMAP
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
config DMA_ENGINE
bool
+config DMA_VIRTUAL_CHANNELS
+ tristate
+
comment "DMA Clients"
depends on DMA_ENGINE
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 640356add0a3..4cf6b128ab9a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG
ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
obj-$(CONFIG_NET_DMA) += iovlock.o
obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
obj-$(CONFIG_DMATEST) += dmatest.o
@@ -30,3 +31,4 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 49ecbbb8932d..6fbeebb9486f 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -86,10 +86,12 @@
#include <asm/hardware/pl080.h>
#include "dmaengine.h"
+#include "virt-dma.h"
#define DRIVER_NAME "pl08xdmac"
static struct amba_driver pl08x_amba_driver;
+struct pl08x_driver_data;
/**
* struct vendor_data - vendor-specific config parameters for PL08x derivatives
@@ -119,6 +121,123 @@ struct pl08x_lli {
};
/**
+ * struct pl08x_bus_data - information of source or destination
+ * busses for a transfer
+ * @addr: current address
+ * @maxwidth: the maximum width of a transfer on this bus
+ * @buswidth: the width of this bus in bytes: 1, 2 or 4
+ */
+struct pl08x_bus_data {
+ dma_addr_t addr;
+ u8 maxwidth;
+ u8 buswidth;
+};
+
+/**
+ * struct pl08x_phy_chan - holder for the physical channels
+ * @id: physical index to this channel
+ * @lock: a lock to use when altering an instance of this struct
+ * @serving: the virtual channel currently being served by this physical
+ * channel
+ * @locked: channel unavailable for the system, e.g. dedicated to secure
+ * world
+ */
+struct pl08x_phy_chan {
+ unsigned int id;
+ void __iomem *base;
+ spinlock_t lock;
+ struct pl08x_dma_chan *serving;
+ bool locked;
+};
+
+/**
+ * struct pl08x_sg - structure containing data per sg
+ * @src_addr: src address of sg
+ * @dst_addr: dst address of sg
+ * @len: transfer len in bytes
+ * @node: node for txd's dsg_list
+ */
+struct pl08x_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ size_t len;
+ struct list_head node;
+};
+
+/**
+ * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
+ * @vd: virtual DMA descriptor
+ * @dsg_list: list of children sg's
+ * @llis_bus: DMA memory address (physical) start for the LLIs
+ * @llis_va: virtual memory address start for the LLIs
+ * @cctl: control reg values for current txd
+ * @ccfg: config reg values for current txd
+ * @done: this marks completed descriptors, which should not have their
+ * mux released.
+ */
+struct pl08x_txd {
+ struct virt_dma_desc vd;
+ struct list_head dsg_list;
+ dma_addr_t llis_bus;
+ struct pl08x_lli *llis_va;
+ /* Default cctl value for LLIs */
+ u32 cctl;
+ /*
+ * Settings to be put into the physical channel when we
+ * trigger this txd. Other registers are in llis_va[0].
+ */
+ u32 ccfg;
+ bool done;
+};
+
+/**
+ * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel
+ * states
+ * @PL08X_CHAN_IDLE: the channel is idle
+ * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
+ * channel and is running a transfer on it
+ * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
+ * channel, but the transfer is currently paused
+ * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
+ * channel to become available (only pertains to memcpy channels)
+ */
+enum pl08x_dma_chan_state {
+ PL08X_CHAN_IDLE,
+ PL08X_CHAN_RUNNING,
+ PL08X_CHAN_PAUSED,
+ PL08X_CHAN_WAITING,
+};
+
+/**
+ * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
+ * @vc: wrappped virtual channel
+ * @phychan: the physical channel utilized by this channel, if there is one
+ * @name: name of channel
+ * @cd: channel platform data
+ * @runtime_addr: address for RX/TX according to the runtime config
+ * @at: active transaction on this channel
+ * @lock: a lock for this channel data
+ * @host: a pointer to the host (internal use)
+ * @state: whether the channel is idle, paused, running etc
+ * @slave: whether this channel is a device (slave) or for memcpy
+ * @signal: the physical DMA request signal which this channel is using
+ * @mux_use: count of descriptors using this DMA request signal setting
+ */
+struct pl08x_dma_chan {
+ struct virt_dma_chan vc;
+ struct pl08x_phy_chan *phychan;
+ const char *name;
+ const struct pl08x_channel_data *cd;
+ struct dma_slave_config cfg;
+ struct pl08x_txd *at;
+ struct pl08x_driver_data *host;
+ enum pl08x_dma_chan_state state;
+ bool slave;
+ int signal;
+ unsigned mux_use;
+};
+
+/**
* struct pl08x_driver_data - the local state holder for the PL08x
* @slave: slave engine for this instance
* @memcpy: memcpy engine for this instance
@@ -128,7 +247,6 @@ struct pl08x_lli {
* @pd: platform data passed in from the platform/machine
* @phy_chans: array of data for the physical channels
* @pool: a pool for the LLI descriptors
- * @pool_ctr: counter of LLIs in the pool
* @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
* fetches
* @mem_buses: set to indicate memory transfers on AHB2.
@@ -143,10 +261,8 @@ struct pl08x_driver_data {
struct pl08x_platform_data *pd;
struct pl08x_phy_chan *phy_chans;
struct dma_pool *pool;
- int pool_ctr;
u8 lli_buses;
u8 mem_buses;
- spinlock_t lock;
};
/*
@@ -162,12 +278,51 @@ struct pl08x_driver_data {
static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
{
- return container_of(chan, struct pl08x_dma_chan, chan);
+ return container_of(chan, struct pl08x_dma_chan, vc.chan);
}
static inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx)
{
- return container_of(tx, struct pl08x_txd, tx);
+ return container_of(tx, struct pl08x_txd, vd.tx);
+}
+
+/*
+ * Mux handling.
+ *
+ * This gives us the DMA request input to the PL08x primecell which the
+ * peripheral described by the channel data will be routed to, possibly
+ * via a board/SoC specific external MUX. One important point to note
+ * here is that this does not depend on the physical channel.
+ */
+static int pl08x_request_mux(struct pl08x_dma_chan *plchan)
+{
+ const struct pl08x_platform_data *pd = plchan->host->pd;
+ int ret;
+
+ if (plchan->mux_use++ == 0 && pd->get_signal) {
+ ret = pd->get_signal(plchan->cd);
+ if (ret < 0) {
+ plchan->mux_use = 0;
+ return ret;
+ }
+
+ plchan->signal = ret;
+ }
+ return 0;
+}
+
+static void pl08x_release_mux(struct pl08x_dma_chan *plchan)
+{
+ const struct pl08x_platform_data *pd = plchan->host->pd;
+
+ if (plchan->signal >= 0) {
+ WARN_ON(plchan->mux_use == 0);
+
+ if (--plchan->mux_use == 0 && pd->put_signal) {
+ pd->put_signal(plchan->cd, plchan->signal);
+ plchan->signal = -1;
+ }
+ }
}
/*
@@ -189,20 +344,25 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
* been set when the LLIs were constructed. Poke them into the hardware
* and start the transfer.
*/
-static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
+static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
{
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_phy_chan *phychan = plchan->phychan;
- struct pl08x_lli *lli = &txd->llis_va[0];
+ struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc);
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_lli *lli;
u32 val;
+ list_del(&txd->vd.node);
+
plchan->at = txd;
/* Wait for channel inactive */
while (pl08x_phy_channel_busy(phychan))
cpu_relax();
+ lli = &txd->llis_va[0];
+
dev_vdbg(&pl08x->adev->dev,
"WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
"clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n",
@@ -311,10 +471,8 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
{
struct pl08x_phy_chan *ch;
struct pl08x_txd *txd;
- unsigned long flags;
size_t bytes = 0;
- spin_lock_irqsave(&plchan->lock, flags);
ch = plchan->phychan;
txd = plchan->at;
@@ -354,18 +512,6 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
}
}
- /* Sum up all queued transactions */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *txdi;
- list_for_each_entry(txdi, &plchan->pend_list, node) {
- struct pl08x_sg *dsg;
- list_for_each_entry(dsg, &txd->dsg_list, node)
- bytes += dsg->len;
- }
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
return bytes;
}
@@ -391,7 +537,6 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
if (!ch->locked && !ch->serving) {
ch->serving = virt_chan;
- ch->signal = -1;
spin_unlock_irqrestore(&ch->lock, flags);
break;
}
@@ -404,25 +549,114 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
return NULL;
}
- pm_runtime_get_sync(&pl08x->adev->dev);
return ch;
}
+/* Mark the physical channel as free. Note, this write is atomic. */
static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
struct pl08x_phy_chan *ch)
{
- unsigned long flags;
+ ch->serving = NULL;
+}
+
+/*
+ * Try to allocate a physical channel. When successful, assign it to
+ * this virtual channel, and initiate the next descriptor. The
+ * virtual channel lock must be held at this point.
+ */
+static void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_phy_chan *ch;
- spin_lock_irqsave(&ch->lock, flags);
+ ch = pl08x_get_phy_channel(pl08x, plchan);
+ if (!ch) {
+ dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
+ plchan->state = PL08X_CHAN_WAITING;
+ return;
+ }
- /* Stop the channel and clear its interrupts */
- pl08x_terminate_phy_chan(pl08x, ch);
+ dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
+ ch->id, plchan->name);
- pm_runtime_put(&pl08x->adev->dev);
+ plchan->phychan = ch;
+ plchan->state = PL08X_CHAN_RUNNING;
+ pl08x_start_next_txd(plchan);
+}
- /* Mark it as free */
- ch->serving = NULL;
- spin_unlock_irqrestore(&ch->lock, flags);
+static void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch,
+ struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+
+ dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n",
+ ch->id, plchan->name);
+
+ /*
+ * We do this without taking the lock; we're really only concerned
+ * about whether this pointer is NULL or not, and we're guaranteed
+ * that this will only be called when it _already_ is non-NULL.
+ */
+ ch->serving = plchan;
+ plchan->phychan = ch;
+ plchan->state = PL08X_CHAN_RUNNING;
+ pl08x_start_next_txd(plchan);
+}
+
+/*
+ * Free a physical DMA channel, potentially reallocating it to another
+ * virtual channel if we have any pending.
+ */
+static void pl08x_phy_free(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_dma_chan *p, *next;
+
+ retry:
+ next = NULL;
+
+ /* Find a waiting virtual channel for the next transfer. */
+ list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node)
+ if (p->state == PL08X_CHAN_WAITING) {
+ next = p;
+ break;
+ }
+
+ if (!next) {
+ list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node)
+ if (p->state == PL08X_CHAN_WAITING) {
+ next = p;
+ break;
+ }
+ }
+
+ /* Ensure that the physical channel is stopped */
+ pl08x_terminate_phy_chan(pl08x, plchan->phychan);
+
+ if (next) {
+ bool success;
+
+ /*
+ * Eww. We know this isn't going to deadlock
+ * but lockdep probably doesn't.
+ */
+ spin_lock(&next->vc.lock);
+ /* Re-check the state now that we have the lock */
+ success = next->state == PL08X_CHAN_WAITING;
+ if (success)
+ pl08x_phy_reassign_start(plchan->phychan, next);
+ spin_unlock(&next->vc.lock);
+
+ /* If the state changed, try to find another channel */
+ if (!success)
+ goto retry;
+ } else {
+ /* No more jobs, so free up the physical channel */
+ pl08x_put_phy_channel(pl08x, plchan->phychan);
+ }
+
+ plchan->phychan = NULL;
+ plchan->state = PL08X_CHAN_IDLE;
}
/*
@@ -585,8 +819,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
return 0;
}
- pl08x->pool_ctr++;
-
bd.txd = txd;
bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
cctl = txd->cctl;
@@ -802,18 +1034,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
return num_llis;
}
-/* You should call this with the struct pl08x lock held */
static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txd)
{
struct pl08x_sg *dsg, *_dsg;
- /* Free the LLI */
if (txd->llis_va)
dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
- pl08x->pool_ctr--;
-
list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) {
list_del(&dsg->node);
kfree(dsg);
@@ -822,133 +1050,75 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
kfree(txd);
}
-static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
- struct pl08x_dma_chan *plchan)
+static void pl08x_unmap_buffers(struct pl08x_txd *txd)
{
- struct pl08x_txd *txdi = NULL;
- struct pl08x_txd *next;
-
- if (!list_empty(&plchan->pend_list)) {
- list_for_each_entry_safe(txdi,
- next, &plchan->pend_list, node) {
- list_del(&txdi->node);
- pl08x_free_txd(pl08x, txdi);
+ struct device *dev = txd->vd.tx.chan->device->dev;
+ struct pl08x_sg *dsg;
+
+ if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
+ else {
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
}
}
+ if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
+ else
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
+ }
}
-/*
- * The DMA ENGINE API
- */
-static int pl08x_alloc_chan_resources(struct dma_chan *chan)
+static void pl08x_desc_free(struct virt_dma_desc *vd)
{
- return 0;
-}
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan);
-static void pl08x_free_chan_resources(struct dma_chan *chan)
-{
+ if (!plchan->slave)
+ pl08x_unmap_buffers(txd);
+
+ if (!txd->done)
+ pl08x_release_mux(plchan);
+
+ pl08x_free_txd(plchan->host, txd);
}
-/*
- * This should be called with the channel plchan->lock held
- */
-static int prep_phy_channel(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
+static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
+ struct pl08x_dma_chan *plchan)
{
- struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_phy_chan *ch;
- int ret;
-
- /* Check if we already have a channel */
- if (plchan->phychan) {
- ch = plchan->phychan;
- goto got_channel;
- }
+ LIST_HEAD(head);
+ struct pl08x_txd *txd;
- ch = pl08x_get_phy_channel(pl08x, plchan);
- if (!ch) {
- /* No physical channel available, cope with it */
- dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
- return -EBUSY;
- }
+ vchan_get_all_descriptors(&plchan->vc, &head);
- /*
- * OK we have a physical channel: for memcpy() this is all we
- * need, but for slaves the physical signals may be muxed!
- * Can the platform allow us to use this channel?
- */
- if (plchan->slave && pl08x->pd->get_signal) {
- ret = pl08x->pd->get_signal(plchan);
- if (ret < 0) {
- dev_dbg(&pl08x->adev->dev,
- "unable to use physical channel %d for transfer on %s due to platform restrictions\n",
- ch->id, plchan->name);
- /* Release physical channel & return */
- pl08x_put_phy_channel(pl08x, ch);
- return -EBUSY;
- }
- ch->signal = ret;
+ while (!list_empty(&head)) {
+ txd = list_first_entry(&head, struct pl08x_txd, vd.node);
+ list_del(&txd->vd.node);
+ pl08x_desc_free(&txd->vd);
}
-
- plchan->phychan = ch;
- dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
- ch->id,
- ch->signal,
- plchan->name);
-
-got_channel:
- /* Assign the flow control signal to this channel */
- if (txd->direction == DMA_MEM_TO_DEV)
- txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT;
- else if (txd->direction == DMA_DEV_TO_MEM)
- txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
-
- plchan->phychan_hold++;
-
- return 0;
}
-static void release_phy_channel(struct pl08x_dma_chan *plchan)
+/*
+ * The DMA ENGINE API
+ */
+static int pl08x_alloc_chan_resources(struct dma_chan *chan)
{
- struct pl08x_driver_data *pl08x = plchan->host;
-
- if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) {
- pl08x->pd->put_signal(plchan);
- plchan->phychan->signal = -1;
- }
- pl08x_put_phy_channel(pl08x, plchan->phychan);
- plchan->phychan = NULL;
+ return 0;
}
-static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
+static void pl08x_free_chan_resources(struct dma_chan *chan)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
- struct pl08x_txd *txd = to_pl08x_txd(tx);
- unsigned long flags;
- dma_cookie_t cookie;
-
- spin_lock_irqsave(&plchan->lock, flags);
- cookie = dma_cookie_assign(tx);
-
- /* Put this onto the pending list */
- list_add_tail(&txd->node, &plchan->pend_list);
-
- /*
- * If there was no physical channel available for this memcpy,
- * stack the request up and indicate that the channel is waiting
- * for a free physical channel.
- */
- if (!plchan->slave && !plchan->phychan) {
- /* Do this memcpy whenever there is a channel ready */
- plchan->state = PL08X_CHAN_WAITING;
- plchan->waiting = txd;
- } else {
- plchan->phychan_hold--;
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- return cookie;
+ /* Ensure all queued descriptors are freed */
+ vchan_free_chan_resources(to_virt_chan(chan));
}
static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
@@ -968,23 +1138,53 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct virt_dma_desc *vd;
+ unsigned long flags;
enum dma_status ret;
+ size_t bytes = 0;
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_SUCCESS)
return ret;
/*
+ * There's no point calculating the residue if there's
+ * no txstate to store the value.
+ */
+ if (!txstate) {
+ if (plchan->state == PL08X_CHAN_PAUSED)
+ ret = DMA_PAUSED;
+ return ret;
+ }
+
+ spin_lock_irqsave(&plchan->vc.lock, flags);
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret != DMA_SUCCESS) {
+ vd = vchan_find_desc(&plchan->vc, cookie);
+ if (vd) {
+ /* On the issued list, so hasn't been processed yet */
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_sg *dsg;
+
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ bytes += dsg->len;
+ } else {
+ bytes = pl08x_getbytes_chan(plchan);
+ }
+ }
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
+
+ /*
* This cookie not complete yet
* Get number of bytes left in the active transactions and queue
*/
- dma_set_residue(txstate, pl08x_getbytes_chan(plchan));
+ dma_set_residue(txstate, bytes);
- if (plchan->state == PL08X_CHAN_PAUSED)
- return DMA_PAUSED;
+ if (plchan->state == PL08X_CHAN_PAUSED && ret == DMA_IN_PROGRESS)
+ ret = DMA_PAUSED;
/* Whether waiting or running, we're in progress */
- return DMA_IN_PROGRESS;
+ return ret;
}
/* PrimeCell DMA extension */
@@ -1080,38 +1280,14 @@ static u32 pl08x_burst(u32 maxburst)
return burst_sizes[i].reg;
}
-static int dma_set_runtime_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan,
+ enum dma_slave_buswidth addr_width, u32 maxburst)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- struct pl08x_driver_data *pl08x = plchan->host;
- enum dma_slave_buswidth addr_width;
- u32 width, burst, maxburst;
- u32 cctl = 0;
-
- if (!plchan->slave)
- return -EINVAL;
-
- /* Transfer direction */
- plchan->runtime_direction = config->direction;
- if (config->direction == DMA_MEM_TO_DEV) {
- addr_width = config->dst_addr_width;
- maxburst = config->dst_maxburst;
- } else if (config->direction == DMA_DEV_TO_MEM) {
- addr_width = config->src_addr_width;
- maxburst = config->src_maxburst;
- } else {
- dev_err(&pl08x->adev->dev,
- "bad runtime_config: alien transfer direction\n");
- return -EINVAL;
- }
+ u32 width, burst, cctl = 0;
width = pl08x_width(addr_width);
- if (width == ~0) {
- dev_err(&pl08x->adev->dev,
- "bad runtime_config: alien address width\n");
- return -EINVAL;
- }
+ if (width == ~0)
+ return ~0;
cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
@@ -1128,28 +1304,23 @@ static int dma_set_runtime_config(struct dma_chan *chan,
cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
- plchan->device_fc = config->device_fc;
+ return pl08x_cctl(cctl);
+}
- if (plchan->runtime_direction == DMA_DEV_TO_MEM) {
- plchan->src_addr = config->src_addr;
- plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
- pl08x_select_bus(plchan->cd->periph_buses,
- pl08x->mem_buses);
- } else {
- plchan->dst_addr = config->dst_addr;
- plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
- pl08x_select_bus(pl08x->mem_buses,
- plchan->cd->periph_buses);
- }
+static int dma_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- dev_dbg(&pl08x->adev->dev,
- "configured channel %s (%s) for %s, data width %d, "
- "maxburst %d words, LE, CCTL=0x%08x\n",
- dma_chan_name(chan), plchan->name,
- (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX",
- addr_width,
- maxburst,
- cctl);
+ if (!plchan->slave)
+ return -EINVAL;
+
+ /* Reject definitely invalid configurations */
+ if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+ config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+ return -EINVAL;
+
+ plchan->cfg = *config;
return 0;
}
@@ -1163,95 +1334,19 @@ static void pl08x_issue_pending(struct dma_chan *chan)
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
unsigned long flags;
- spin_lock_irqsave(&plchan->lock, flags);
- /* Something is already active, or we're waiting for a channel... */
- if (plchan->at || plchan->state == PL08X_CHAN_WAITING) {
- spin_unlock_irqrestore(&plchan->lock, flags);
- return;
- }
-
- /* Take the first element in the queue and execute it */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *next;
-
- next = list_first_entry(&plchan->pend_list,
- struct pl08x_txd,
- node);
- list_del(&next->node);
- plchan->state = PL08X_CHAN_RUNNING;
-
- pl08x_start_txd(plchan, next);
+ spin_lock_irqsave(&plchan->vc.lock, flags);
+ if (vchan_issue_pending(&plchan->vc)) {
+ if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING)
+ pl08x_phy_alloc_and_start(plchan);
}
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-}
-
-static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
-{
- struct pl08x_driver_data *pl08x = plchan->host;
- unsigned long flags;
- int num_llis, ret;
-
- num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
- if (!num_llis) {
- spin_lock_irqsave(&plchan->lock, flags);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&plchan->lock, flags);
-
- /*
- * See if we already have a physical channel allocated,
- * else this is the time to try to get one.
- */
- ret = prep_phy_channel(plchan, txd);
- if (ret) {
- /*
- * No physical channel was available.
- *
- * memcpy transfers can be sorted out at submission time.
- *
- * Slave transfers may have been denied due to platform
- * channel muxing restrictions. Since there is no guarantee
- * that this will ever be resolved, and the signal must be
- * acquired AFTER acquiring the physical channel, we will let
- * them be NACK:ed with -EBUSY here. The drivers can retry
- * the prep() call if they are eager on doing this using DMA.
- */
- if (plchan->slave) {
- pl08x_free_txd_list(pl08x, plchan);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
- return -EBUSY;
- }
- } else
- /*
- * Else we're all set, paused and ready to roll, status
- * will switch to PL08X_CHAN_RUNNING when we call
- * issue_pending(). If there is something running on the
- * channel already we don't change its state.
- */
- if (plchan->state == PL08X_CHAN_IDLE)
- plchan->state = PL08X_CHAN_PAUSED;
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- return 0;
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
}
-static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
- unsigned long flags)
+static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
{
struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
if (txd) {
- dma_async_tx_descriptor_init(&txd->tx, &plchan->chan);
- txd->tx.flags = flags;
- txd->tx.tx_submit = pl08x_tx_submit;
- INIT_LIST_HEAD(&txd->node);
INIT_LIST_HEAD(&txd->dsg_list);
/* Always enable error and terminal interrupts */
@@ -1274,7 +1369,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
struct pl08x_sg *dsg;
int ret;
- txd = pl08x_get_txd(plchan, flags);
+ txd = pl08x_get_txd(plchan);
if (!txd) {
dev_err(&pl08x->adev->dev,
"%s no memory for descriptor\n", __func__);
@@ -1290,14 +1385,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
}
list_add_tail(&dsg->node, &txd->dsg_list);
- txd->direction = DMA_NONE;
dsg->src_addr = src;
dsg->dst_addr = dest;
dsg->len = len;
/* Set platform data for m2m */
txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl = pl08x->pd->memcpy_channel.cctl &
+ txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy &
~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
/* Both to be incremented or the code will break */
@@ -1307,11 +1401,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
pl08x->mem_buses);
- ret = pl08x_prep_channel_resources(plchan, txd);
- if (ret)
+ ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+ if (!ret) {
+ pl08x_free_txd(pl08x, txd);
return NULL;
+ }
- return &txd->tx;
+ return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
}
static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
@@ -1324,36 +1420,40 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct pl08x_txd *txd;
struct pl08x_sg *dsg;
struct scatterlist *sg;
+ enum dma_slave_buswidth addr_width;
dma_addr_t slave_addr;
int ret, tmp;
+ u8 src_buses, dst_buses;
+ u32 maxburst, cctl;
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
__func__, sg_dma_len(sgl), plchan->name);
- txd = pl08x_get_txd(plchan, flags);
+ txd = pl08x_get_txd(plchan);
if (!txd) {
dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
return NULL;
}
- if (direction != plchan->runtime_direction)
- dev_err(&pl08x->adev->dev, "%s DMA setup does not match "
- "the direction configured for the PrimeCell\n",
- __func__);
-
/*
* Set up addresses, the PrimeCell configured address
* will take precedence since this may configure the
* channel target address dynamically at runtime.
*/
- txd->direction = direction;
-
if (direction == DMA_MEM_TO_DEV) {
- txd->cctl = plchan->dst_cctl;
- slave_addr = plchan->dst_addr;
+ cctl = PL080_CONTROL_SRC_INCR;
+ slave_addr = plchan->cfg.dst_addr;
+ addr_width = plchan->cfg.dst_addr_width;
+ maxburst = plchan->cfg.dst_maxburst;
+ src_buses = pl08x->mem_buses;
+ dst_buses = plchan->cd->periph_buses;
} else if (direction == DMA_DEV_TO_MEM) {
- txd->cctl = plchan->src_cctl;
- slave_addr = plchan->src_addr;
+ cctl = PL080_CONTROL_DST_INCR;
+ slave_addr = plchan->cfg.src_addr;
+ addr_width = plchan->cfg.src_addr_width;
+ maxburst = plchan->cfg.src_maxburst;
+ src_buses = plchan->cd->periph_buses;
+ dst_buses = pl08x->mem_buses;
} else {
pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev,
@@ -1361,7 +1461,17 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
return NULL;
}
- if (plchan->device_fc)
+ cctl |= pl08x_get_cctl(plchan, addr_width, maxburst);
+ if (cctl == ~0) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev,
+ "DMA slave configuration botched?\n");
+ return NULL;
+ }
+
+ txd->cctl = cctl | pl08x_select_bus(src_buses, dst_buses);
+
+ if (plchan->cfg.device_fc)
tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER :
PL080_FLOW_PER2MEM_PER;
else
@@ -1370,9 +1480,28 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ ret = pl08x_request_mux(plchan);
+ if (ret < 0) {
+ pl08x_free_txd(pl08x, txd);
+ dev_dbg(&pl08x->adev->dev,
+ "unable to mux for transfer on %s due to platform restrictions\n",
+ plchan->name);
+ return NULL;
+ }
+
+ dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
+ plchan->signal, plchan->name);
+
+ /* Assign the flow control signal to this channel */
+ if (direction == DMA_MEM_TO_DEV)
+ txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
+ else
+ txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
+
for_each_sg(sgl, sg, sg_len, tmp) {
dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
if (!dsg) {
+ pl08x_release_mux(plchan);
pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
__func__);
@@ -1390,11 +1519,14 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
}
}
- ret = pl08x_prep_channel_resources(plchan, txd);
- if (ret)
+ ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+ if (!ret) {
+ pl08x_release_mux(plchan);
+ pl08x_free_txd(pl08x, txd);
return NULL;
+ }
- return &txd->tx;
+ return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
}
static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -1415,9 +1547,9 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* Anything succeeds on channels with no physical allocation and
* no queued transfers.
*/
- spin_lock_irqsave(&plchan->lock, flags);
+ spin_lock_irqsave(&plchan->vc.lock, flags);
if (!plchan->phychan && !plchan->at) {
- spin_unlock_irqrestore(&plchan->lock, flags);
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
return 0;
}
@@ -1426,18 +1558,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
plchan->state = PL08X_CHAN_IDLE;
if (plchan->phychan) {
- pl08x_terminate_phy_chan(pl08x, plchan->phychan);
-
/*
* Mark physical channel as free and free any slave
* signal
*/
- release_phy_channel(plchan);
- plchan->phychan_hold = 0;
+ pl08x_phy_free(plchan);
}
/* Dequeue jobs and free LLIs */
if (plchan->at) {
- pl08x_free_txd(pl08x, plchan->at);
+ pl08x_desc_free(&plchan->at->vd);
plchan->at = NULL;
}
/* Dequeue jobs not yet fired as well */
@@ -1457,7 +1586,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
break;
}
- spin_unlock_irqrestore(&plchan->lock, flags);
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
return ret;
}
@@ -1494,123 +1623,6 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
}
-static void pl08x_unmap_buffers(struct pl08x_txd *txd)
-{
- struct device *dev = txd->tx.chan->device->dev;
- struct pl08x_sg *dsg;
-
- if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
- if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_single(dev, dsg->src_addr, dsg->len,
- DMA_TO_DEVICE);
- else {
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_page(dev, dsg->src_addr, dsg->len,
- DMA_TO_DEVICE);
- }
- }
- if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
- if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_single(dev, dsg->dst_addr, dsg->len,
- DMA_FROM_DEVICE);
- else
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_page(dev, dsg->dst_addr, dsg->len,
- DMA_FROM_DEVICE);
- }
-}
-
-static void pl08x_tasklet(unsigned long data)
-{
- struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
- struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_txd *txd;
- unsigned long flags;
-
- spin_lock_irqsave(&plchan->lock, flags);
-
- txd = plchan->at;
- plchan->at = NULL;
-
- if (txd) {
- /* Update last completed */
- dma_cookie_complete(&txd->tx);
- }
-
- /* If a new descriptor is queued, set it up plchan->at is NULL here */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *next;
-
- next = list_first_entry(&plchan->pend_list,
- struct pl08x_txd,
- node);
- list_del(&next->node);
-
- pl08x_start_txd(plchan, next);
- } else if (plchan->phychan_hold) {
- /*
- * This channel is still in use - we have a new txd being
- * prepared and will soon be queued. Don't give up the
- * physical channel.
- */
- } else {
- struct pl08x_dma_chan *waiting = NULL;
-
- /*
- * No more jobs, so free up the physical channel
- * Free any allocated signal on slave transfers too
- */
- release_phy_channel(plchan);
- plchan->state = PL08X_CHAN_IDLE;
-
- /*
- * And NOW before anyone else can grab that free:d up
- * physical channel, see if there is some memcpy pending
- * that seriously needs to start because of being stacked
- * up while we were choking the physical channels with data.
- */
- list_for_each_entry(waiting, &pl08x->memcpy.channels,
- chan.device_node) {
- if (waiting->state == PL08X_CHAN_WAITING &&
- waiting->waiting != NULL) {
- int ret;
-
- /* This should REALLY not fail now */
- ret = prep_phy_channel(waiting,
- waiting->waiting);
- BUG_ON(ret);
- waiting->phychan_hold--;
- waiting->state = PL08X_CHAN_RUNNING;
- waiting->waiting = NULL;
- pl08x_issue_pending(&waiting->chan);
- break;
- }
- }
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- if (txd) {
- dma_async_tx_callback callback = txd->tx.callback;
- void *callback_param = txd->tx.callback_param;
-
- /* Don't try to unmap buffers on slave channels */
- if (!plchan->slave)
- pl08x_unmap_buffers(txd);
-
- /* Free the descriptor */
- spin_lock_irqsave(&plchan->lock, flags);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- /* Callback to signal completion */
- if (callback)
- callback(callback_param);
- }
-}
-
static irqreturn_t pl08x_irq(int irq, void *dev)
{
struct pl08x_driver_data *pl08x = dev;
@@ -1635,6 +1647,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
/* Locate physical channel */
struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
struct pl08x_dma_chan *plchan = phychan->serving;
+ struct pl08x_txd *tx;
if (!plchan) {
dev_err(&pl08x->adev->dev,
@@ -1643,8 +1656,29 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
continue;
}
- /* Schedule tasklet on this channel */
- tasklet_schedule(&plchan->tasklet);
+ spin_lock(&plchan->vc.lock);
+ tx = plchan->at;
+ if (tx) {
+ plchan->at = NULL;
+ /*
+ * This descriptor is done, release its mux
+ * reservation.
+ */
+ pl08x_release_mux(plchan);
+ tx->done = true;
+ vchan_cookie_complete(&tx->vd);
+
+ /*
+ * And start the next descriptor (if any),
+ * otherwise free this channel.
+ */
+ if (vchan_next_desc(&plchan->vc))
+ pl08x_start_next_txd(plchan);
+ else
+ pl08x_phy_free(plchan);
+ }
+ spin_unlock(&plchan->vc.lock);
+
mask |= (1 << i);
}
}
@@ -1654,16 +1688,10 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
{
- u32 cctl = pl08x_cctl(chan->cd->cctl);
-
chan->slave = true;
chan->name = chan->cd->bus_id;
- chan->src_addr = chan->cd->addr;
- chan->dst_addr = chan->cd->addr;
- chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
- pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
- chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
- pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+ chan->cfg.src_addr = chan->cd->addr;
+ chan->cfg.dst_addr = chan->cd->addr;
}
/*
@@ -1693,6 +1721,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan->host = pl08x;
chan->state = PL08X_CHAN_IDLE;
+ chan->signal = -1;
if (slave) {
chan->cd = &pl08x->pd->slave_channels[i];
@@ -1705,26 +1734,12 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
return -ENOMEM;
}
}
- if (chan->cd->circular_buffer) {
- dev_err(&pl08x->adev->dev,
- "channel %s: circular buffers not supported\n",
- chan->name);
- kfree(chan);
- continue;
- }
dev_dbg(&pl08x->adev->dev,
"initialize virtual channel \"%s\"\n",
chan->name);
- chan->chan.device = dmadev;
- dma_cookie_init(&chan->chan);
-
- spin_lock_init(&chan->lock);
- INIT_LIST_HEAD(&chan->pend_list);
- tasklet_init(&chan->tasklet, pl08x_tasklet,
- (unsigned long) chan);
-
- list_add_tail(&chan->chan.device_node, &dmadev->channels);
+ chan->vc.desc_free = pl08x_desc_free;
+ vchan_init(&chan->vc, dmadev);
}
dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n",
i, slave ? "slave" : "memcpy");
@@ -1737,8 +1752,8 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev)
struct pl08x_dma_chan *next;
list_for_each_entry_safe(chan,
- next, &dmadev->channels, chan.device_node) {
- list_del(&chan->chan.device_node);
+ next, &dmadev->channels, vc.chan.device_node) {
+ list_del(&chan->vc.chan.device_node);
kfree(chan);
}
}
@@ -1791,7 +1806,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "\nPL08x virtual memcpy channels:\n");
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
- list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) {
+ list_for_each_entry(chan, &pl08x->memcpy.channels, vc.chan.device_node) {
seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1799,7 +1814,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "\nPL08x virtual slave channels:\n");
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
- list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) {
+ list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) {
seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1851,9 +1866,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto out_no_pl08x;
}
- pm_runtime_set_active(&adev->dev);
- pm_runtime_enable(&adev->dev);
-
/* Initialize memcpy engine */
dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
pl08x->memcpy.dev = &adev->dev;
@@ -1903,8 +1915,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto out_no_lli_pool;
}
- spin_lock_init(&pl08x->lock);
-
pl08x->base = ioremap(adev->res.start, resource_size(&adev->res));
if (!pl08x->base) {
ret = -ENOMEM;
@@ -1942,7 +1952,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
ch->id = i;
ch->base = pl08x->base + PL080_Cx_BASE(i);
spin_lock_init(&ch->lock);
- ch->signal = -1;
/*
* Nomadik variants can have channels that are locked
@@ -2007,7 +2016,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
amba_part(adev), amba_rev(adev),
(unsigned long long)adev->res.start, adev->irq[0]);
- pm_runtime_put(&adev->dev);
return 0;
out_no_slave_reg:
@@ -2026,9 +2034,6 @@ out_no_ioremap:
dma_pool_destroy(pl08x->pool);
out_no_lli_pool:
out_no_platdata:
- pm_runtime_put(&adev->dev);
- pm_runtime_disable(&adev->dev);
-
kfree(pl08x);
out_no_pl08x:
amba_release_regions(adev);
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 000000000000..ae0561826137
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,669 @@
+/*
+ * OMAP DMAengine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/omap-dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+ struct dma_device ddev;
+ spinlock_t lock;
+ struct tasklet_struct task;
+ struct list_head pending;
+};
+
+struct omap_chan {
+ struct virt_dma_chan vc;
+ struct list_head node;
+
+ struct dma_slave_config cfg;
+ unsigned dma_sig;
+ bool cyclic;
+
+ int dma_ch;
+ struct omap_desc *desc;
+ unsigned sgidx;
+};
+
+struct omap_sg {
+ dma_addr_t addr;
+ uint32_t en; /* number of elements (24-bit) */
+ uint32_t fn; /* number of frames (16-bit) */
+};
+
+struct omap_desc {
+ struct virt_dma_desc vd;
+ enum dma_transfer_direction dir;
+ dma_addr_t dev_addr;
+
+ int16_t fi; /* for OMAP_DMA_SYNC_PACKET */
+ uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
+ uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
+ uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
+ uint8_t periph_port; /* Peripheral port */
+
+ unsigned sglen;
+ struct omap_sg sg[0];
+};
+
+static const unsigned es_bytes[] = {
+ [OMAP_DMA_DATA_TYPE_S8] = 1,
+ [OMAP_DMA_DATA_TYPE_S16] = 2,
+ [OMAP_DMA_DATA_TYPE_S32] = 4,
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+ return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+ return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+ kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+ unsigned idx)
+{
+ struct omap_sg *sg = d->sg + idx;
+
+ if (d->dir == DMA_DEV_TO_MEM)
+ omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+ else
+ omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+
+ omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+ d->sync_mode, c->dma_sig, d->sync_type);
+
+ omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+ struct omap_desc *d;
+
+ if (!vd) {
+ c->desc = NULL;
+ return;
+ }
+
+ list_del(&vd->node);
+
+ c->desc = d = to_omap_dma_desc(&vd->tx);
+ c->sgidx = 0;
+
+ if (d->dir == DMA_DEV_TO_MEM)
+ omap_set_dma_src_params(c->dma_ch, d->periph_port,
+ OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+ else
+ omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+ OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+
+ omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+ struct omap_chan *c = data;
+ struct omap_desc *d;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ d = c->desc;
+ if (d) {
+ if (!c->cyclic) {
+ if (++c->sgidx < d->sglen) {
+ omap_dma_start_sg(c, d, c->sgidx);
+ } else {
+ omap_dma_start_desc(c);
+ vchan_cookie_complete(&d->vd);
+ }
+ } else {
+ vchan_cyclic_callback(&d->vd);
+ }
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels. We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+ struct omap_dmadev *d = (struct omap_dmadev *)data;
+ LIST_HEAD(head);
+
+ spin_lock_irq(&d->lock);
+ list_splice_tail_init(&d->pending, &head);
+ spin_unlock_irq(&d->lock);
+
+ while (!list_empty(&head)) {
+ struct omap_chan *c = list_first_entry(&head,
+ struct omap_chan, node);
+
+ spin_lock_irq(&c->vc.lock);
+ list_del_init(&c->node);
+ omap_dma_start_desc(c);
+ spin_unlock_irq(&c->vc.lock);
+ }
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+
+ dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+ return omap_request_dma(c->dma_sig, "DMA engine",
+ omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+
+ vchan_free_chan_resources(&c->vc);
+ omap_free_dma(c->dma_ch);
+
+ dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static size_t omap_dma_sg_size(struct omap_sg *sg)
+{
+ return sg->en * sg->fn;
+}
+
+static size_t omap_dma_desc_size(struct omap_desc *d)
+{
+ unsigned i;
+ size_t size;
+
+ for (size = i = 0; i < d->sglen; i++)
+ size += omap_dma_sg_size(&d->sg[i]);
+
+ return size * es_bytes[d->es];
+}
+
+static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
+{
+ unsigned i;
+ size_t size, es_size = es_bytes[d->es];
+
+ for (size = i = 0; i < d->sglen; i++) {
+ size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size;
+
+ if (size)
+ size += this_size;
+ else if (addr >= d->sg[i].addr &&
+ addr < d->sg[i].addr + this_size)
+ size += d->sg[i].addr + this_size - addr;
+ }
+ return size;
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ struct virt_dma_desc *vd;
+ enum dma_status ret;
+ unsigned long flags;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_SUCCESS || !txstate)
+ return ret;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ vd = vchan_find_desc(&c->vc, cookie);
+ if (vd) {
+ txstate->residue = omap_dma_desc_size(to_omap_dma_desc(&vd->tx));
+ } else if (c->desc && c->desc->vd.tx.cookie == cookie) {
+ struct omap_desc *d = c->desc;
+ dma_addr_t pos;
+
+ if (d->dir == DMA_MEM_TO_DEV)
+ pos = omap_get_dma_src_pos(c->dma_ch);
+ else if (d->dir == DMA_DEV_TO_MEM)
+ pos = omap_get_dma_dst_pos(c->dma_ch);
+ else
+ pos = 0;
+
+ txstate->residue = omap_dma_desc_size_pos(d, pos);
+ } else {
+ txstate->residue = 0;
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return ret;
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc) && !c->desc) {
+ struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+ spin_lock(&d->lock);
+ if (list_empty(&c->node))
+ list_add_tail(&c->node, &d->pending);
+ spin_unlock(&d->lock);
+ tasklet_schedule(&d->task);
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+ enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct scatterlist *sgent;
+ struct omap_desc *d;
+ dma_addr_t dev_addr;
+ unsigned i, j = 0, es, en, frame_bytes, sync_type;
+ u32 burst;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_addr = c->cfg.src_addr;
+ dev_width = c->cfg.src_addr_width;
+ burst = c->cfg.src_maxburst;
+ sync_type = OMAP_DMA_SRC_SYNC;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_addr = c->cfg.dst_addr;
+ dev_width = c->cfg.dst_addr_width;
+ burst = c->cfg.dst_maxburst;
+ sync_type = OMAP_DMA_DST_SYNC;
+ } else {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ /* Bus width translates to the element size (ES) */
+ switch (dev_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ es = OMAP_DMA_DATA_TYPE_S8;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S32;
+ break;
+ default: /* not reached */
+ return NULL;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ d->dir = dir;
+ d->dev_addr = dev_addr;
+ d->es = es;
+ d->sync_mode = OMAP_DMA_SYNC_FRAME;
+ d->sync_type = sync_type;
+ d->periph_port = OMAP_DMA_PORT_TIPB;
+
+ /*
+ * Build our scatterlist entries: each contains the address,
+ * the number of elements (EN) in each frame, and the number of
+ * frames (FN). Number of bytes for this entry = ES * EN * FN.
+ *
+ * Burst size translates to number of elements with frame sync.
+ * Note: DMA engine defines burst to be the number of dev-width
+ * transfers.
+ */
+ en = burst;
+ frame_bytes = es_bytes[es] * en;
+ for_each_sg(sgl, sgent, sglen, i) {
+ d->sg[j].addr = sg_dma_address(sgent);
+ d->sg[j].en = en;
+ d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+ j++;
+ }
+
+ d->sglen = j;
+
+ return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction dir, void *context)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct omap_desc *d;
+ dma_addr_t dev_addr;
+ unsigned es, sync_type;
+ u32 burst;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_addr = c->cfg.src_addr;
+ dev_width = c->cfg.src_addr_width;
+ burst = c->cfg.src_maxburst;
+ sync_type = OMAP_DMA_SRC_SYNC;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_addr = c->cfg.dst_addr;
+ dev_width = c->cfg.dst_addr_width;
+ burst = c->cfg.dst_maxburst;
+ sync_type = OMAP_DMA_DST_SYNC;
+ } else {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ /* Bus width translates to the element size (ES) */
+ switch (dev_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ es = OMAP_DMA_DATA_TYPE_S8;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S32;
+ break;
+ default: /* not reached */
+ return NULL;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ d->dir = dir;
+ d->dev_addr = dev_addr;
+ d->fi = burst;
+ d->es = es;
+ d->sync_mode = OMAP_DMA_SYNC_PACKET;
+ d->sync_type = sync_type;
+ d->periph_port = OMAP_DMA_PORT_MPUI;
+ d->sg[0].addr = buf_addr;
+ d->sg[0].en = period_len / es_bytes[es];
+ d->sg[0].fn = buf_len / period_len;
+ d->sglen = 1;
+
+ if (!c->cyclic) {
+ c->cyclic = true;
+ omap_dma_link_lch(c->dma_ch, c->dma_ch);
+ omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+ omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
+ }
+
+ if (!cpu_class_is_omap1()) {
+ omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ }
+
+ return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+ if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+ cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+ return -EINVAL;
+
+ memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+ return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+ struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+
+ /* Prevent this channel being scheduled */
+ spin_lock(&d->lock);
+ list_del_init(&c->node);
+ spin_unlock(&d->lock);
+
+ /*
+ * Stop DMA activity: we assume the callback will not be called
+ * after omap_stop_dma() returns (even if it does, it will see
+ * c->desc is NULL and exit.)
+ */
+ if (c->desc) {
+ c->desc = NULL;
+ omap_stop_dma(c->dma_ch);
+ }
+
+ if (c->cyclic) {
+ c->cyclic = false;
+ omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
+ }
+
+ vchan_get_all_descriptors(&c->vc, &head);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_dma_desc_free_list(&c->vc, &head);
+
+ return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+ /* FIXME: not supported by platform private API */
+ return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+ /* FIXME: not supported by platform private API */
+ return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ int ret;
+
+ switch (cmd) {
+ case DMA_SLAVE_CONFIG:
+ ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+ break;
+
+ case DMA_TERMINATE_ALL:
+ ret = omap_dma_terminate_all(c);
+ break;
+
+ case DMA_PAUSE:
+ ret = omap_dma_pause(c);
+ break;
+
+ case DMA_RESUME:
+ ret = omap_dma_resume(c);
+ break;
+
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+ struct omap_chan *c;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ c->dma_sig = dma_sig;
+ c->vc.desc_free = omap_dma_desc_free;
+ vchan_init(&c->vc, &od->ddev);
+ INIT_LIST_HEAD(&c->node);
+
+ od->ddev.chancnt++;
+
+ return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+ tasklet_kill(&od->task);
+ while (!list_empty(&od->ddev.channels)) {
+ struct omap_chan *c = list_first_entry(&od->ddev.channels,
+ struct omap_chan, vc.chan.device_node);
+
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
+ kfree(c);
+ }
+ kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+ struct omap_dmadev *od;
+ int rc, i;
+
+ od = kzalloc(sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;
+
+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
+ od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+ od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+ od->ddev.device_tx_status = omap_dma_tx_status;
+ od->ddev.device_issue_pending = omap_dma_issue_pending;
+ od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+ od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
+ od->ddev.device_control = omap_dma_control;
+ od->ddev.dev = &pdev->dev;
+ INIT_LIST_HEAD(&od->ddev.channels);
+ INIT_LIST_HEAD(&od->pending);
+ spin_lock_init(&od->lock);
+
+ tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+ for (i = 0; i < 127; i++) {
+ rc = omap_dma_chan_init(od, i);
+ if (rc) {
+ omap_dma_free(od);
+ return rc;
+ }
+ }
+
+ rc = dma_async_device_register(&od->ddev);
+ if (rc) {
+ pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+ rc);
+ omap_dma_free(od);
+ } else {
+ platform_set_drvdata(pdev, od);
+ }
+
+ dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+ return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+ struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+ dma_async_device_unregister(&od->ddev);
+ omap_dma_free(od);
+
+ return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+ .probe = omap_dma_probe,
+ .remove = omap_dma_remove,
+ .driver = {
+ .name = "omap-dma-engine",
+ .owner = THIS_MODULE,
+ },
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ if (chan->device->dev->driver == &omap_dma_driver.driver) {
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ unsigned req = *(unsigned *)param;
+
+ return req == c->dma_sig;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+ .name = "omap-dma-engine",
+ .id = -1,
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+ int rc = platform_driver_register(&omap_dma_driver);
+
+ if (rc == 0) {
+ pdev = platform_device_register_full(&omap_dma_dev_info);
+ if (IS_ERR(pdev)) {
+ platform_driver_unregister(&omap_dma_driver);
+ rc = PTR_ERR(pdev);
+ }
+ }
+ return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index ec78ccef9132..f5a73606217e 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include "virt-dma.h"
+
#define NR_PHY_CHAN 6
#define DMA_ALIGN 3
#define DMA_MAX_SIZE 0x1fff
@@ -72,12 +74,13 @@ struct sa11x0_dma_sg {
};
struct sa11x0_dma_desc {
- struct dma_async_tx_descriptor tx;
+ struct virt_dma_desc vd;
+
u32 ddar;
size_t size;
+ unsigned period;
+ bool cyclic;
- /* maybe protected by c->lock */
- struct list_head node;
unsigned sglen;
struct sa11x0_dma_sg sg[0];
};
@@ -85,15 +88,11 @@ struct sa11x0_dma_desc {
struct sa11x0_dma_phy;
struct sa11x0_dma_chan {
- struct dma_chan chan;
- spinlock_t lock;
- dma_cookie_t lc;
+ struct virt_dma_chan vc;
- /* protected by c->lock */
+ /* protected by c->vc.lock */
struct sa11x0_dma_phy *phy;
enum dma_status status;
- struct list_head desc_submitted;
- struct list_head desc_issued;
/* protected by d->lock */
struct list_head node;
@@ -109,7 +108,7 @@ struct sa11x0_dma_phy {
struct sa11x0_dma_chan *vchan;
- /* Protected by c->lock */
+ /* Protected by c->vc.lock */
unsigned sg_load;
struct sa11x0_dma_desc *txd_load;
unsigned sg_done;
@@ -127,13 +126,12 @@ struct sa11x0_dma_dev {
spinlock_t lock;
struct tasklet_struct task;
struct list_head chan_pending;
- struct list_head desc_complete;
struct sa11x0_dma_phy phy[NR_PHY_CHAN];
};
static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
{
- return container_of(chan, struct sa11x0_dma_chan, chan);
+ return container_of(chan, struct sa11x0_dma_chan, vc.chan);
}
static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
@@ -141,27 +139,26 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
return container_of(dmadev, struct sa11x0_dma_dev, slave);
}
-static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
{
- return container_of(tx, struct sa11x0_dma_desc, tx);
+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+
+ return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL;
}
-static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+static void sa11x0_dma_free_desc(struct virt_dma_desc *vd)
{
- if (list_empty(&c->desc_issued))
- return NULL;
-
- return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+ kfree(container_of(vd, struct sa11x0_dma_desc, vd));
}
static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
{
- list_del(&txd->node);
+ list_del(&txd->vd.node);
p->txd_load = txd;
p->sg_load = 0;
dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
- p->num, txd, txd->tx.cookie, txd->ddar);
+ p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar);
}
static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
@@ -183,19 +180,24 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
return;
if (p->sg_load == txd->sglen) {
- struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
+ if (!txd->cyclic) {
+ struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
- /*
- * We have reached the end of the current descriptor.
- * Peek at the next descriptor, and if compatible with
- * the current, start processing it.
- */
- if (txn && txn->ddar == txd->ddar) {
- txd = txn;
- sa11x0_dma_start_desc(p, txn);
+ /*
+ * We have reached the end of the current descriptor.
+ * Peek at the next descriptor, and if compatible with
+ * the current, start processing it.
+ */
+ if (txn && txn->ddar == txd->ddar) {
+ txd = txn;
+ sa11x0_dma_start_desc(p, txn);
+ } else {
+ p->txd_load = NULL;
+ return;
+ }
} else {
- p->txd_load = NULL;
- return;
+ /* Cyclic: reset back to beginning */
+ p->sg_load = 0;
}
}
@@ -229,21 +231,21 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
struct sa11x0_dma_desc *txd = p->txd_done;
if (++p->sg_done == txd->sglen) {
- struct sa11x0_dma_dev *d = p->dev;
-
- dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
- p->num, p->txd_done, p->txd_done->tx.cookie);
-
- c->lc = txd->tx.cookie;
+ if (!txd->cyclic) {
+ vchan_cookie_complete(&txd->vd);
- spin_lock(&d->lock);
- list_add_tail(&txd->node, &d->desc_complete);
- spin_unlock(&d->lock);
+ p->sg_done = 0;
+ p->txd_done = p->txd_load;
- p->sg_done = 0;
- p->txd_done = p->txd_load;
+ if (!p->txd_done)
+ tasklet_schedule(&p->dev->task);
+ } else {
+ if ((p->sg_done % txd->period) == 0)
+ vchan_cyclic_callback(&txd->vd);
- tasklet_schedule(&d->task);
+ /* Cyclic: reset back to beginning */
+ p->sg_done = 0;
+ }
}
sa11x0_dma_start_sg(p, c);
@@ -280,7 +282,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
if (c) {
unsigned long flags;
- spin_lock_irqsave(&c->lock, flags);
+ spin_lock_irqsave(&c->vc.lock, flags);
/*
* Now that we're holding the lock, check that the vchan
* really is associated with this pchan before touching the
@@ -294,7 +296,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
if (dcsr & DCSR_DONEB)
sa11x0_dma_complete(p, c);
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
}
return IRQ_HANDLED;
@@ -332,28 +334,15 @@ static void sa11x0_dma_tasklet(unsigned long arg)
struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
struct sa11x0_dma_phy *p;
struct sa11x0_dma_chan *c;
- struct sa11x0_dma_desc *txd, *txn;
- LIST_HEAD(head);
unsigned pch, pch_alloc = 0;
dev_dbg(d->slave.dev, "tasklet enter\n");
- /* Get the completed tx descriptors */
- spin_lock_irq(&d->lock);
- list_splice_init(&d->desc_complete, &head);
- spin_unlock_irq(&d->lock);
-
- list_for_each_entry(txd, &head, node) {
- c = to_sa11x0_dma_chan(txd->tx.chan);
-
- dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
- c, txd, txd->tx.cookie);
-
- spin_lock_irq(&c->lock);
+ list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
+ spin_lock_irq(&c->vc.lock);
p = c->phy;
- if (p) {
- if (!p->txd_done)
- sa11x0_dma_start_txd(c);
+ if (p && !p->txd_done) {
+ sa11x0_dma_start_txd(c);
if (!p->txd_done) {
/* No current txd associated with this channel */
dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
@@ -363,7 +352,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
p->vchan = NULL;
}
}
- spin_unlock_irq(&c->lock);
+ spin_unlock_irq(&c->vc.lock);
}
spin_lock_irq(&d->lock);
@@ -380,7 +369,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
/* Mark this channel allocated */
p->vchan = c;
- dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+ dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc);
}
}
spin_unlock_irq(&d->lock);
@@ -390,42 +379,18 @@ static void sa11x0_dma_tasklet(unsigned long arg)
p = &d->phy[pch];
c = p->vchan;
- spin_lock_irq(&c->lock);
+ spin_lock_irq(&c->vc.lock);
c->phy = p;
sa11x0_dma_start_txd(c);
- spin_unlock_irq(&c->lock);
+ spin_unlock_irq(&c->vc.lock);
}
}
- /* Now free the completed tx descriptor, and call their callbacks */
- list_for_each_entry_safe(txd, txn, &head, node) {
- dma_async_tx_callback callback = txd->tx.callback;
- void *callback_param = txd->tx.callback_param;
-
- dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
- txd, txd->tx.cookie);
-
- kfree(txd);
-
- if (callback)
- callback(callback_param);
- }
-
dev_dbg(d->slave.dev, "tasklet exit\n");
}
-static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
-{
- struct sa11x0_dma_desc *txd, *txn;
-
- list_for_each_entry_safe(txd, txn, head, node) {
- dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
- kfree(txd);
- }
-}
-
static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
{
return 0;
@@ -436,18 +401,12 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
unsigned long flags;
- LIST_HEAD(head);
- spin_lock_irqsave(&c->lock, flags);
- spin_lock(&d->lock);
+ spin_lock_irqsave(&d->lock, flags);
list_del_init(&c->node);
- spin_unlock(&d->lock);
-
- list_splice_tail_init(&c->desc_submitted, &head);
- list_splice_tail_init(&c->desc_issued, &head);
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&d->lock, flags);
- sa11x0_dma_desc_free(d, &head);
+ vchan_free_chan_resources(&c->vc);
}
static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
@@ -472,33 +431,47 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
struct sa11x0_dma_phy *p;
- struct sa11x0_dma_desc *txd;
- dma_cookie_t last_used, last_complete;
+ struct virt_dma_desc *vd;
unsigned long flags;
enum dma_status ret;
- size_t bytes = 0;
-
- last_used = c->chan.cookie;
- last_complete = c->lc;
- ret = dma_async_is_complete(cookie, last_complete, last_used);
- if (ret == DMA_SUCCESS) {
- dma_set_tx_state(state, last_complete, last_used, 0);
+ ret = dma_cookie_status(&c->vc.chan, cookie, state);
+ if (ret == DMA_SUCCESS)
return ret;
- }
- spin_lock_irqsave(&c->lock, flags);
+ if (!state)
+ return c->status;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
p = c->phy;
- ret = c->status;
- if (p) {
- dma_addr_t addr = sa11x0_dma_pos(p);
- dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+ /*
+ * If the cookie is on our issue queue, then the residue is
+ * its total size.
+ */
+ vd = vchan_find_desc(&c->vc, cookie);
+ if (vd) {
+ state->residue = container_of(vd, struct sa11x0_dma_desc, vd)->size;
+ } else if (!p) {
+ state->residue = 0;
+ } else {
+ struct sa11x0_dma_desc *txd;
+ size_t bytes = 0;
- txd = p->txd_done;
+ if (p->txd_done && p->txd_done->vd.tx.cookie == cookie)
+ txd = p->txd_done;
+ else if (p->txd_load && p->txd_load->vd.tx.cookie == cookie)
+ txd = p->txd_load;
+ else
+ txd = NULL;
+
+ ret = c->status;
if (txd) {
+ dma_addr_t addr = sa11x0_dma_pos(p);
unsigned i;
+ dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+
for (i = 0; i < txd->sglen; i++) {
dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n",
i, txd->sg[i].addr, txd->sg[i].len);
@@ -521,17 +494,11 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
bytes += txd->sg[i].len;
}
}
- if (txd != p->txd_load && p->txd_load)
- bytes += p->txd_load->size;
- }
- list_for_each_entry(txd, &c->desc_issued, node) {
- bytes += txd->size;
+ state->residue = bytes;
}
- spin_unlock_irqrestore(&c->lock, flags);
-
- dma_set_tx_state(state, last_complete, last_used, bytes);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
- dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
+ dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", state->residue);
return ret;
}
@@ -547,40 +514,20 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan)
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
unsigned long flags;
- spin_lock_irqsave(&c->lock, flags);
- list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
- if (!list_empty(&c->desc_issued)) {
- spin_lock(&d->lock);
- if (!c->phy && list_empty(&c->node)) {
- list_add_tail(&c->node, &d->chan_pending);
- tasklet_schedule(&d->task);
- dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc)) {
+ if (!c->phy) {
+ spin_lock(&d->lock);
+ if (list_empty(&c->node)) {
+ list_add_tail(&c->node, &d->chan_pending);
+ tasklet_schedule(&d->task);
+ dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc);
+ }
+ spin_unlock(&d->lock);
}
- spin_unlock(&d->lock);
} else
- dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
- spin_unlock_irqrestore(&c->lock, flags);
-}
-
-static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
-{
- struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
- struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
- unsigned long flags;
-
- spin_lock_irqsave(&c->lock, flags);
- c->chan.cookie += 1;
- if (c->chan.cookie < 0)
- c->chan.cookie = 1;
- txd->tx.cookie = c->chan.cookie;
-
- list_add_tail(&txd->node, &c->desc_submitted);
- spin_unlock_irqrestore(&c->lock, flags);
-
- dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
- c, txd, txd->tx.cookie);
-
- return txd->tx.cookie;
+ dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
}
static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
@@ -596,7 +543,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
/* SA11x0 channels can only operate in their native direction */
if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
- c, c->ddar, dir);
+ &c->vc, c->ddar, dir);
return NULL;
}
@@ -612,14 +559,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
if (addr & DMA_ALIGN) {
dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
- c, addr);
+ &c->vc, addr);
return NULL;
}
}
txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
if (!txd) {
- dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+ dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
return NULL;
}
@@ -655,17 +602,73 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
} while (len);
}
- dma_async_tx_descriptor_init(&txd->tx, &c->chan);
- txd->tx.flags = flags;
- txd->tx.tx_submit = sa11x0_dma_tx_submit;
txd->ddar = c->ddar;
txd->size = size;
txd->sglen = j;
dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
- c, txd, txd->size, txd->sglen);
+ &c->vc, &txd->vd, txd->size, txd->sglen);
- return &txd->tx;
+ return vchan_tx_prep(&c->vc, &txd->vd, flags);
+}
+
+static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
+ enum dma_transfer_direction dir, void *context)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_desc *txd;
+ unsigned i, j, k, sglen, sgperiod;
+
+ /* SA11x0 channels can only operate in their native direction */
+ if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
+ dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
+ &c->vc, c->ddar, dir);
+ return NULL;
+ }
+
+ sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN);
+ sglen = size * sgperiod / period;
+
+ /* Do not allow zero-sized txds */
+ if (sglen == 0)
+ return NULL;
+
+ txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC);
+ if (!txd) {
+ dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
+ return NULL;
+ }
+
+ for (i = k = 0; i < size / period; i++) {
+ size_t tlen, len = period;
+
+ for (j = 0; j < sgperiod; j++, k++) {
+ tlen = len;
+
+ if (tlen > DMA_MAX_SIZE) {
+ unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN);
+ tlen = (tlen / mult) & ~DMA_ALIGN;
+ }
+
+ txd->sg[k].addr = addr;
+ txd->sg[k].len = tlen;
+ addr += tlen;
+ len -= tlen;
+ }
+
+ WARN_ON(len != 0);
+ }
+
+ WARN_ON(k != sglen);
+
+ txd->ddar = c->ddar;
+ txd->size = size;
+ txd->sglen = sglen;
+ txd->cyclic = 1;
+ txd->period = sgperiod;
+
+ return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
@@ -695,8 +698,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c
if (maxburst == 8)
ddar |= DDAR_BS;
- dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
- c, addr, width, maxburst);
+ dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+ &c->vc, addr, width, maxburst);
c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
@@ -718,16 +721,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
case DMA_TERMINATE_ALL:
- dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+ dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc);
/* Clear the tx descriptor lists */
- spin_lock_irqsave(&c->lock, flags);
- list_splice_tail_init(&c->desc_submitted, &head);
- list_splice_tail_init(&c->desc_issued, &head);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ vchan_get_all_descriptors(&c->vc, &head);
p = c->phy;
if (p) {
- struct sa11x0_dma_desc *txd, *txn;
-
dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
/* vchan is assigned to a pchan - stop the channel */
writel(DCSR_RUN | DCSR_IE |
@@ -735,17 +735,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
DCSR_STRTB | DCSR_DONEB,
p->base + DMA_DCSR_C);
- list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
- if (txd->tx.chan == &c->chan)
- list_move(&txd->node, &head);
-
if (p->txd_load) {
if (p->txd_load != p->txd_done)
- list_add_tail(&p->txd_load->node, &head);
+ list_add_tail(&p->txd_load->vd.node, &head);
p->txd_load = NULL;
}
if (p->txd_done) {
- list_add_tail(&p->txd_done->node, &head);
+ list_add_tail(&p->txd_done->vd.node, &head);
p->txd_done = NULL;
}
c->phy = NULL;
@@ -754,14 +750,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
spin_unlock(&d->lock);
tasklet_schedule(&d->task);
}
- spin_unlock_irqrestore(&c->lock, flags);
- sa11x0_dma_desc_free(d, &head);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_dma_desc_free_list(&c->vc, &head);
ret = 0;
break;
case DMA_PAUSE:
- dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
- spin_lock_irqsave(&c->lock, flags);
+ dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
+ spin_lock_irqsave(&c->vc.lock, flags);
if (c->status == DMA_IN_PROGRESS) {
c->status = DMA_PAUSED;
@@ -774,26 +770,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
spin_unlock(&d->lock);
}
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
ret = 0;
break;
case DMA_RESUME:
- dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
- spin_lock_irqsave(&c->lock, flags);
+ dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
+ spin_lock_irqsave(&c->vc.lock, flags);
if (c->status == DMA_PAUSED) {
c->status = DMA_IN_PROGRESS;
p = c->phy;
if (p) {
writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
- } else if (!list_empty(&c->desc_issued)) {
+ } else if (!list_empty(&c->vc.desc_issued)) {
spin_lock(&d->lock);
list_add_tail(&c->node, &d->chan_pending);
spin_unlock(&d->lock);
}
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
ret = 0;
break;
@@ -853,15 +849,13 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
return -ENOMEM;
}
- c->chan.device = dmadev;
c->status = DMA_IN_PROGRESS;
c->ddar = chan_desc[i].ddar;
c->name = chan_desc[i].name;
- spin_lock_init(&c->lock);
- INIT_LIST_HEAD(&c->desc_submitted);
- INIT_LIST_HEAD(&c->desc_issued);
INIT_LIST_HEAD(&c->node);
- list_add_tail(&c->chan.device_node, &dmadev->channels);
+
+ c->vc.desc_free = sa11x0_dma_free_desc;
+ vchan_init(&c->vc, dmadev);
}
return dma_async_device_register(dmadev);
@@ -890,8 +884,9 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
{
struct sa11x0_dma_chan *c, *cn;
- list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
- list_del(&c->chan.device_node);
+ list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
kfree(c);
}
}
@@ -915,7 +910,6 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
spin_lock_init(&d->lock);
INIT_LIST_HEAD(&d->chan_pending);
- INIT_LIST_HEAD(&d->desc_complete);
d->base = ioremap(res->start, resource_size(res));
if (!d->base) {
@@ -947,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
}
dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
+ dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
+ d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic;
ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
if (ret) {
dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
new file mode 100644
index 000000000000..6f80432a3f0a
--- /dev/null
+++ b/drivers/dma/virt-dma.c
@@ -0,0 +1,123 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct virt_dma_desc, tx);
+}
+
+dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+ struct virt_dma_desc *vd = to_virt_desc(tx);
+ unsigned long flags;
+ dma_cookie_t cookie;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ cookie = dma_cookie_assign(tx);
+
+ list_add_tail(&vd->node, &vc->desc_submitted);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
+ vc, vd, cookie);
+
+ return cookie;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_submit);
+
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
+ dma_cookie_t cookie)
+{
+ struct virt_dma_desc *vd;
+
+ list_for_each_entry(vd, &vc->desc_issued, node)
+ if (vd->tx.cookie == cookie)
+ return vd;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(vchan_find_desc);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+static void vchan_complete(unsigned long arg)
+{
+ struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+ struct virt_dma_desc *vd;
+ dma_async_tx_callback cb = NULL;
+ void *cb_data = NULL;
+ LIST_HEAD(head);
+
+ spin_lock_irq(&vc->lock);
+ list_splice_tail_init(&vc->desc_completed, &head);
+ vd = vc->cyclic;
+ if (vd) {
+ vc->cyclic = NULL;
+ cb = vd->tx.callback;
+ cb_data = vd->tx.callback_param;
+ }
+ spin_unlock_irq(&vc->lock);
+
+ if (cb)
+ cb(cb_data);
+
+ while (!list_empty(&head)) {
+ vd = list_first_entry(&head, struct virt_dma_desc, node);
+ cb = vd->tx.callback;
+ cb_data = vd->tx.callback_param;
+
+ list_del(&vd->node);
+
+ vc->desc_free(vd);
+
+ if (cb)
+ cb(cb_data);
+ }
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
+{
+ while (!list_empty(head)) {
+ struct virt_dma_desc *vd = list_first_entry(head,
+ struct virt_dma_desc, node);
+ list_del(&vd->node);
+ dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+ vc->desc_free(vd);
+ }
+}
+EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
+{
+ dma_cookie_init(&vc->chan);
+
+ spin_lock_init(&vc->lock);
+ INIT_LIST_HEAD(&vc->desc_submitted);
+ INIT_LIST_HEAD(&vc->desc_issued);
+ INIT_LIST_HEAD(&vc->desc_completed);
+
+ tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
+
+ vc->chan.device = dmadev;
+ list_add_tail(&vc->chan.device_node, &dmadev->channels);
+}
+EXPORT_SYMBOL_GPL(vchan_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
new file mode 100644
index 000000000000..85c19d63f9fb
--- /dev/null
+++ b/drivers/dma/virt-dma.h
@@ -0,0 +1,152 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef VIRT_DMA_H
+#define VIRT_DMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+
+#include "dmaengine.h"
+
+struct virt_dma_desc {
+ struct dma_async_tx_descriptor tx;
+ /* protected by vc.lock */
+ struct list_head node;
+};
+
+struct virt_dma_chan {
+ struct dma_chan chan;
+ struct tasklet_struct task;
+ void (*desc_free)(struct virt_dma_desc *);
+
+ spinlock_t lock;
+
+ /* protected by vc.lock */
+ struct list_head desc_submitted;
+ struct list_head desc_issued;
+ struct list_head desc_completed;
+
+ struct virt_dma_desc *cyclic;
+};
+
+static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct virt_dma_chan, chan);
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t);
+
+/**
+ * vchan_tx_prep - prepare a descriptor
+ * vc: virtual channel allocating this descriptor
+ * vd: virtual descriptor to prepare
+ * tx_flags: flags argument passed in to prepare function
+ */
+static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc,
+ struct virt_dma_desc *vd, unsigned long tx_flags)
+{
+ extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+
+ dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
+ vd->tx.flags = tx_flags;
+ vd->tx.tx_submit = vchan_tx_submit;
+
+ return &vd->tx;
+}
+
+/**
+ * vchan_issue_pending - move submitted descriptors to issued list
+ * vc: virtual channel to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline bool vchan_issue_pending(struct virt_dma_chan *vc)
+{
+ list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued);
+ return !list_empty(&vc->desc_issued);
+}
+
+/**
+ * vchan_cookie_complete - report completion of a descriptor
+ * vd: virtual descriptor to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
+{
+ struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+ dma_cookie_complete(&vd->tx);
+ dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
+ vd, vd->tx.cookie);
+ list_add_tail(&vd->node, &vc->desc_completed);
+
+ tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_cyclic_callback - report the completion of a period
+ * vd: virtual descriptor
+ */
+static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
+{
+ struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+ vc->cyclic = vd;
+ tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_next_desc - peek at the next descriptor to be processed
+ * vc: virtual channel to obtain descriptor from
+ *
+ * vc.lock must be held by caller
+ */
+static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
+{
+ if (list_empty(&vc->desc_issued))
+ return NULL;
+
+ return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node);
+}
+
+/**
+ * vchan_get_all_descriptors - obtain all submitted and issued descriptors
+ * vc: virtual channel to get descriptors from
+ * head: list of descriptors found
+ *
+ * vc.lock must be held by caller
+ *
+ * Removes all submitted and issued descriptors from internal lists, and
+ * provides a list of all descriptors found
+ */
+static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
+ struct list_head *head)
+{
+ list_splice_tail_init(&vc->desc_submitted, head);
+ list_splice_tail_init(&vc->desc_issued, head);
+ list_splice_tail_init(&vc->desc_completed, head);
+}
+
+static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
+{
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&vc->lock, flags);
+ vchan_get_all_descriptors(vc, &head);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ vchan_dma_desc_free_list(vc, &head);
+}
+
+#endif
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index fdffa1beca17..409b92b8d346 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -7,7 +7,7 @@
menuconfig EDAC
bool "EDAC (Error Detection And Correction) reporting"
depends on HAS_IOMEM
- depends on X86 || PPC || TILE
+ depends on X86 || PPC || TILE || ARM
help
EDAC is designed to report errors in the core system.
These are low-level errors that are reported in the CPU or
@@ -31,6 +31,14 @@ if EDAC
comment "Reporting subsystems"
+config EDAC_LEGACY_SYSFS
+ bool "EDAC legacy sysfs"
+ default y
+ help
+ Enable the compatibility sysfs nodes.
+ Use 'Y' if your edac utilities aren't ported to work with the newer
+ structures.
+
config EDAC_DEBUG
bool "Debugging"
help
@@ -294,4 +302,18 @@ config EDAC_TILE
Support for error detection and correction on the
Tilera memory controller.
+config EDAC_HIGHBANK_MC
+ tristate "Highbank Memory Controller"
+ depends on EDAC_MM_EDAC && ARCH_HIGHBANK
+ help
+ Support for error detection and correction on the
+ Calxeda Highbank memory controller.
+
+config EDAC_HIGHBANK_L2
+ tristate "Highbank L2 Cache"
+ depends on EDAC_MM_EDAC && ARCH_HIGHBANK
+ help
+ Support for error detection and correction on the
+ Calxeda Highbank memory controller.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 196a63dd37c5..7e5129a733f8 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -55,3 +55,6 @@ obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o
obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o
obj-$(CONFIG_EDAC_TILE) += tile_edac.o
+
+obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
+obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 7be9b7288e90..5a297a26211d 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -321,8 +321,8 @@ found:
return edac_mc_find((int)node_id);
err_no_match:
- debugf2("sys_addr 0x%lx doesn't match any node\n",
- (unsigned long)sys_addr);
+ edac_dbg(2, "sys_addr 0x%lx doesn't match any node\n",
+ (unsigned long)sys_addr);
return NULL;
}
@@ -393,15 +393,15 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr)
mask = ~mask;
if ((input_addr & mask) == (base & mask)) {
- debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n",
- (unsigned long)input_addr, csrow,
- pvt->mc_node_id);
+ edac_dbg(2, "InputAddr 0x%lx matches csrow %d (node %d)\n",
+ (unsigned long)input_addr, csrow,
+ pvt->mc_node_id);
return csrow;
}
}
- debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n",
- (unsigned long)input_addr, pvt->mc_node_id);
+ edac_dbg(2, "no matching csrow for InputAddr 0x%lx (MC node %d)\n",
+ (unsigned long)input_addr, pvt->mc_node_id);
return -1;
}
@@ -430,20 +430,20 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
/* only revE and later have the DRAM Hole Address Register */
if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
- debugf1(" revision %d for node %d does not support DHAR\n",
- pvt->ext_model, pvt->mc_node_id);
+ edac_dbg(1, " revision %d for node %d does not support DHAR\n",
+ pvt->ext_model, pvt->mc_node_id);
return 1;
}
/* valid for Fam10h and above */
if (boot_cpu_data.x86 >= 0x10 && !dhar_mem_hoist_valid(pvt)) {
- debugf1(" Dram Memory Hoisting is DISABLED on this system\n");
+ edac_dbg(1, " Dram Memory Hoisting is DISABLED on this system\n");
return 1;
}
if (!dhar_valid(pvt)) {
- debugf1(" Dram Memory Hoisting is DISABLED on this node %d\n",
- pvt->mc_node_id);
+ edac_dbg(1, " Dram Memory Hoisting is DISABLED on this node %d\n",
+ pvt->mc_node_id);
return 1;
}
@@ -475,9 +475,9 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
else
*hole_offset = k8_dhar_offset(pvt);
- debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
- pvt->mc_node_id, (unsigned long)*hole_base,
- (unsigned long)*hole_offset, (unsigned long)*hole_size);
+ edac_dbg(1, " DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n",
+ pvt->mc_node_id, (unsigned long)*hole_base,
+ (unsigned long)*hole_offset, (unsigned long)*hole_size);
return 0;
}
@@ -528,10 +528,9 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
/* use DHAR to translate SysAddr to DramAddr */
dram_addr = sys_addr - hole_offset;
- debugf2("using DHAR to translate SysAddr 0x%lx to "
- "DramAddr 0x%lx\n",
- (unsigned long)sys_addr,
- (unsigned long)dram_addr);
+ edac_dbg(2, "using DHAR to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
+ (unsigned long)sys_addr,
+ (unsigned long)dram_addr);
return dram_addr;
}
@@ -548,9 +547,8 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
*/
dram_addr = (sys_addr & GENMASK(0, 39)) - dram_base;
- debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
- "DramAddr 0x%lx\n", (unsigned long)sys_addr,
- (unsigned long)dram_addr);
+ edac_dbg(2, "using DRAM Base register to translate SysAddr 0x%lx to DramAddr 0x%lx\n",
+ (unsigned long)sys_addr, (unsigned long)dram_addr);
return dram_addr;
}
@@ -586,9 +584,9 @@ static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
input_addr = ((dram_addr >> intlv_shift) & GENMASK(12, 35)) +
(dram_addr & 0xfff);
- debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
- intlv_shift, (unsigned long)dram_addr,
- (unsigned long)input_addr);
+ edac_dbg(2, " Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
+ intlv_shift, (unsigned long)dram_addr,
+ (unsigned long)input_addr);
return input_addr;
}
@@ -604,8 +602,8 @@ static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
input_addr =
dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
- debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
- (unsigned long)sys_addr, (unsigned long)input_addr);
+ edac_dbg(2, "SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
+ (unsigned long)sys_addr, (unsigned long)input_addr);
return input_addr;
}
@@ -637,8 +635,8 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
intlv_shift = num_node_interleave_bits(dram_intlv_en(pvt, 0));
if (intlv_shift == 0) {
- debugf1(" InputAddr 0x%lx translates to DramAddr of "
- "same value\n", (unsigned long)input_addr);
+ edac_dbg(1, " InputAddr 0x%lx translates to DramAddr of same value\n",
+ (unsigned long)input_addr);
return input_addr;
}
@@ -649,9 +647,9 @@ static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
intlv_sel = dram_intlv_sel(pvt, node_id) & ((1 << intlv_shift) - 1);
dram_addr = bits + (intlv_sel << 12);
- debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
- "(%d node interleave bits)\n", (unsigned long)input_addr,
- (unsigned long)dram_addr, intlv_shift);
+ edac_dbg(1, "InputAddr 0x%lx translates to DramAddr 0x%lx (%d node interleave bits)\n",
+ (unsigned long)input_addr,
+ (unsigned long)dram_addr, intlv_shift);
return dram_addr;
}
@@ -673,9 +671,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
(dram_addr < (hole_base + hole_size))) {
sys_addr = dram_addr + hole_offset;
- debugf1("using DHAR to translate DramAddr 0x%lx to "
- "SysAddr 0x%lx\n", (unsigned long)dram_addr,
- (unsigned long)sys_addr);
+ edac_dbg(1, "using DHAR to translate DramAddr 0x%lx to SysAddr 0x%lx\n",
+ (unsigned long)dram_addr,
+ (unsigned long)sys_addr);
return sys_addr;
}
@@ -697,9 +695,9 @@ static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
*/
sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
- debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
- pvt->mc_node_id, (unsigned long)dram_addr,
- (unsigned long)sys_addr);
+ edac_dbg(1, " Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
+ pvt->mc_node_id, (unsigned long)dram_addr,
+ (unsigned long)sys_addr);
return sys_addr;
}
@@ -768,49 +766,48 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *, u8);
static void amd64_dump_dramcfg_low(u32 dclr, int chan)
{
- debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
+ edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
- debugf1(" DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
- (dclr & BIT(16)) ? "un" : "",
- (dclr & BIT(19)) ? "yes" : "no");
+ edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
+ (dclr & BIT(16)) ? "un" : "",
+ (dclr & BIT(19)) ? "yes" : "no");
- debugf1(" PAR/ERR parity: %s\n",
- (dclr & BIT(8)) ? "enabled" : "disabled");
+ edac_dbg(1, " PAR/ERR parity: %s\n",
+ (dclr & BIT(8)) ? "enabled" : "disabled");
if (boot_cpu_data.x86 == 0x10)
- debugf1(" DCT 128bit mode width: %s\n",
- (dclr & BIT(11)) ? "128b" : "64b");
+ edac_dbg(1, " DCT 128bit mode width: %s\n",
+ (dclr & BIT(11)) ? "128b" : "64b");
- debugf1(" x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
- (dclr & BIT(12)) ? "yes" : "no",
- (dclr & BIT(13)) ? "yes" : "no",
- (dclr & BIT(14)) ? "yes" : "no",
- (dclr & BIT(15)) ? "yes" : "no");
+ edac_dbg(1, " x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
+ (dclr & BIT(12)) ? "yes" : "no",
+ (dclr & BIT(13)) ? "yes" : "no",
+ (dclr & BIT(14)) ? "yes" : "no",
+ (dclr & BIT(15)) ? "yes" : "no");
}
/* Display and decode various NB registers for debug purposes. */
static void dump_misc_regs(struct amd64_pvt *pvt)
{
- debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
+ edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
- debugf1(" NB two channel DRAM capable: %s\n",
- (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
+ edac_dbg(1, " NB two channel DRAM capable: %s\n",
+ (pvt->nbcap & NBCAP_DCT_DUAL) ? "yes" : "no");
- debugf1(" ECC capable: %s, ChipKill ECC capable: %s\n",
- (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
- (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
+ edac_dbg(1, " ECC capable: %s, ChipKill ECC capable: %s\n",
+ (pvt->nbcap & NBCAP_SECDED) ? "yes" : "no",
+ (pvt->nbcap & NBCAP_CHIPKILL) ? "yes" : "no");
amd64_dump_dramcfg_low(pvt->dclr0, 0);
- debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
+ edac_dbg(1, "F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
- debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
- "offset: 0x%08x\n",
- pvt->dhar, dhar_base(pvt),
- (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
- : f10_dhar_offset(pvt));
+ edac_dbg(1, "F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, offset: 0x%08x\n",
+ pvt->dhar, dhar_base(pvt),
+ (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt)
+ : f10_dhar_offset(pvt));
- debugf1(" DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
+ edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
amd64_debug_display_dimm_sizes(pvt, 0);
@@ -857,15 +854,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt)
u32 *base1 = &pvt->csels[1].csbases[cs];
if (!amd64_read_dct_pci_cfg(pvt, reg0, base0))
- debugf0(" DCSB0[%d]=0x%08x reg: F2x%x\n",
- cs, *base0, reg0);
+ edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n",
+ cs, *base0, reg0);
if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
continue;
if (!amd64_read_dct_pci_cfg(pvt, reg1, base1))
- debugf0(" DCSB1[%d]=0x%08x reg: F2x%x\n",
- cs, *base1, reg1);
+ edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n",
+ cs, *base1, reg1);
}
for_each_chip_select_mask(cs, 0, pvt) {
@@ -875,15 +872,15 @@ static void read_dct_base_mask(struct amd64_pvt *pvt)
u32 *mask1 = &pvt->csels[1].csmasks[cs];
if (!amd64_read_dct_pci_cfg(pvt, reg0, mask0))
- debugf0(" DCSM0[%d]=0x%08x reg: F2x%x\n",
- cs, *mask0, reg0);
+ edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n",
+ cs, *mask0, reg0);
if (boot_cpu_data.x86 == 0xf || dct_ganging_enabled(pvt))
continue;
if (!amd64_read_dct_pci_cfg(pvt, reg1, mask1))
- debugf0(" DCSM1[%d]=0x%08x reg: F2x%x\n",
- cs, *mask1, reg1);
+ edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n",
+ cs, *mask1, reg1);
}
}
@@ -1049,24 +1046,22 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
if (!src_mci) {
amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
(unsigned long)sys_addr);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset, syndrome,
-1, -1, -1,
- EDAC_MOD_STR,
"failed to map error addr to a node",
- NULL);
+ "");
return;
}
/* Now map the sys_addr to a CSROW */
csrow = sys_addr_to_csrow(src_mci, sys_addr);
if (csrow < 0) {
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset, syndrome,
-1, -1, -1,
- EDAC_MOD_STR,
"failed to map error addr to a csrow",
- NULL);
+ "");
return;
}
@@ -1082,12 +1077,11 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - "
"possible error reporting race\n",
syndrome);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset, syndrome,
csrow, -1, -1,
- EDAC_MOD_STR,
"unknown syndrome - possible error reporting race",
- NULL);
+ "");
return;
}
} else {
@@ -1102,10 +1096,10 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
channel = ((sys_addr & BIT(3)) != 0);
}
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci, 1,
page, offset, syndrome,
csrow, channel, -1,
- EDAC_MOD_STR, "", NULL);
+ "", "");
}
static int ddr2_cs_size(unsigned i, bool dct_width)
@@ -1193,7 +1187,7 @@ static int f1x_early_channel_count(struct amd64_pvt *pvt)
* Need to check DCT0[0] and DCT1[0] to see if only one of them has
* their CSEnable bit on. If so, then SINGLE DIMM case.
*/
- debugf0("Data width is not 128 bits - need more decoding\n");
+ edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
/*
* Check DRAM Bank Address Mapping values for each DIMM to see if there
@@ -1272,25 +1266,24 @@ static void read_dram_ctl_register(struct amd64_pvt *pvt)
return;
if (!amd64_read_dct_pci_cfg(pvt, DCT_SEL_LO, &pvt->dct_sel_lo)) {
- debugf0("F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
- pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
+ edac_dbg(0, "F2x110 (DCTSelLow): 0x%08x, High range addrs at: 0x%x\n",
+ pvt->dct_sel_lo, dct_sel_baseaddr(pvt));
- debugf0(" DCTs operate in %s mode.\n",
- (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
+ edac_dbg(0, " DCTs operate in %s mode\n",
+ (dct_ganging_enabled(pvt) ? "ganged" : "unganged"));
if (!dct_ganging_enabled(pvt))
- debugf0(" Address range split per DCT: %s\n",
- (dct_high_range_enabled(pvt) ? "yes" : "no"));
+ edac_dbg(0, " Address range split per DCT: %s\n",
+ (dct_high_range_enabled(pvt) ? "yes" : "no"));
- debugf0(" data interleave for ECC: %s, "
- "DRAM cleared since last warm reset: %s\n",
- (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
- (dct_memory_cleared(pvt) ? "yes" : "no"));
+ edac_dbg(0, " data interleave for ECC: %s, DRAM cleared since last warm reset: %s\n",
+ (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
+ (dct_memory_cleared(pvt) ? "yes" : "no"));
- debugf0(" channel interleave: %s, "
- "interleave bits selector: 0x%x\n",
- (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
- dct_sel_interleave_addr(pvt));
+ edac_dbg(0, " channel interleave: %s, "
+ "interleave bits selector: 0x%x\n",
+ (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
+ dct_sel_interleave_addr(pvt));
}
amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
@@ -1428,7 +1421,7 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
pvt = mci->pvt_info;
- debugf1("input addr: 0x%llx, DCT: %d\n", in_addr, dct);
+ edac_dbg(1, "input addr: 0x%llx, DCT: %d\n", in_addr, dct);
for_each_chip_select(csrow, dct, pvt) {
if (!csrow_enabled(csrow, dct, pvt))
@@ -1436,19 +1429,18 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u32 nid, u8 dct)
get_cs_base_and_mask(pvt, csrow, dct, &cs_base, &cs_mask);
- debugf1(" CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
- csrow, cs_base, cs_mask);
+ edac_dbg(1, " CSROW=%d CSBase=0x%llx CSMask=0x%llx\n",
+ csrow, cs_base, cs_mask);
cs_mask = ~cs_mask;
- debugf1(" (InputAddr & ~CSMask)=0x%llx "
- "(CSBase & ~CSMask)=0x%llx\n",
- (in_addr & cs_mask), (cs_base & cs_mask));
+ edac_dbg(1, " (InputAddr & ~CSMask)=0x%llx (CSBase & ~CSMask)=0x%llx\n",
+ (in_addr & cs_mask), (cs_base & cs_mask));
if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
cs_found = f10_process_possible_spare(pvt, dct, csrow);
- debugf1(" MATCH csrow=%d\n", cs_found);
+ edac_dbg(1, " MATCH csrow=%d\n", cs_found);
break;
}
}
@@ -1505,8 +1497,8 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
u8 intlv_en = dram_intlv_en(pvt, range);
u32 intlv_sel = dram_intlv_sel(pvt, range);
- debugf1("(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
- range, sys_addr, get_dram_limit(pvt, range));
+ edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
+ range, sys_addr, get_dram_limit(pvt, range));
if (dhar_valid(pvt) &&
dhar_base(pvt) <= sys_addr &&
@@ -1562,7 +1554,7 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
(chan_addr & 0xfff);
}
- debugf1(" Normalized DCT addr: 0x%llx\n", chan_addr);
+ edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, channel);
@@ -1616,12 +1608,11 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
if (csrow < 0) {
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset, syndrome,
-1, -1, -1,
- EDAC_MOD_STR,
"failed to map error addr to a csrow",
- NULL);
+ "");
return;
}
@@ -1633,10 +1624,10 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
if (dct_ganging_enabled(pvt))
chan = get_channel_from_ecc_syndrome(mci, syndrome);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset, syndrome,
csrow, chan, -1,
- EDAC_MOD_STR, "", NULL);
+ "", "");
}
/*
@@ -1664,7 +1655,8 @@ static void amd64_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
dcsb = (ctrl && !dct_ganging_enabled(pvt)) ? pvt->csels[1].csbases
: pvt->csels[0].csbases;
- debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n", ctrl, dbam);
+ edac_dbg(1, "F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
+ ctrl, dbam);
edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
@@ -1840,7 +1832,7 @@ static int decode_syndrome(u16 syndrome, u16 *vectors, unsigned num_vecs,
}
}
- debugf0("syndrome(%x) not found\n", syndrome);
+ edac_dbg(0, "syndrome(%x) not found\n", syndrome);
return -1;
}
@@ -1917,12 +1909,11 @@ static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
/* Ensure that the Error Address is VALID */
if (!(m->status & MCI_STATUS_ADDRV)) {
amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, 0,
-1, -1, -1,
- EDAC_MOD_STR,
"HW has no ERROR_ADDRESS available",
- NULL);
+ "");
return;
}
@@ -1946,12 +1937,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
if (!(m->status & MCI_STATUS_ADDRV)) {
amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, 0,
-1, -1, -1,
- EDAC_MOD_STR,
"HW has no ERROR_ADDRESS available",
- NULL);
+ "");
return;
}
@@ -1966,11 +1956,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
if (!src_mci) {
amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
(unsigned long)sys_addr);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, offset, 0,
-1, -1, -1,
- EDAC_MOD_STR,
- "ERROR ADDRESS NOT mapped to a MC", NULL);
+ "ERROR ADDRESS NOT mapped to a MC",
+ "");
return;
}
@@ -1980,17 +1970,16 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
if (csrow < 0) {
amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
(unsigned long)sys_addr);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, offset, 0,
-1, -1, -1,
- EDAC_MOD_STR,
"ERROR ADDRESS NOT mapped to CS",
- NULL);
+ "");
} else {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, offset, 0,
csrow, -1, -1,
- EDAC_MOD_STR, "", NULL);
+ "", "");
}
}
@@ -2047,9 +2036,9 @@ static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f3_id)
return -ENODEV;
}
- debugf1("F1: %s\n", pci_name(pvt->F1));
- debugf1("F2: %s\n", pci_name(pvt->F2));
- debugf1("F3: %s\n", pci_name(pvt->F3));
+ edac_dbg(1, "F1: %s\n", pci_name(pvt->F1));
+ edac_dbg(1, "F2: %s\n", pci_name(pvt->F2));
+ edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
return 0;
}
@@ -2076,15 +2065,15 @@ static void read_mc_regs(struct amd64_pvt *pvt)
* those are Read-As-Zero
*/
rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
- debugf0(" TOP_MEM: 0x%016llx\n", pvt->top_mem);
+ edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem);
/* check first whether TOP_MEM2 is enabled */
rdmsrl(MSR_K8_SYSCFG, msr_val);
if (msr_val & (1U << 21)) {
rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
- debugf0(" TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
+ edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
} else
- debugf0(" TOP_MEM2 disabled.\n");
+ edac_dbg(0, " TOP_MEM2 disabled\n");
amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap);
@@ -2100,17 +2089,17 @@ static void read_mc_regs(struct amd64_pvt *pvt)
if (!rw)
continue;
- debugf1(" DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
- range,
- get_dram_base(pvt, range),
- get_dram_limit(pvt, range));
+ edac_dbg(1, " DRAM range[%d], base: 0x%016llx; limit: 0x%016llx\n",
+ range,
+ get_dram_base(pvt, range),
+ get_dram_limit(pvt, range));
- debugf1(" IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
- dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
- (rw & 0x1) ? "R" : "-",
- (rw & 0x2) ? "W" : "-",
- dram_intlv_sel(pvt, range),
- dram_dst_node(pvt, range));
+ edac_dbg(1, " IntlvEn=%s; Range access: %s%s IntlvSel=%d DstNode=%d\n",
+ dram_intlv_en(pvt, range) ? "Enabled" : "Disabled",
+ (rw & 0x1) ? "R" : "-",
+ (rw & 0x2) ? "W" : "-",
+ dram_intlv_sel(pvt, range),
+ dram_dst_node(pvt, range));
}
read_dct_base_mask(pvt);
@@ -2191,9 +2180,9 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
- debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
- debugf0(" nr_pages/channel= %u channel-count = %d\n",
- nr_pages, pvt->channel_count);
+ edac_dbg(0, " (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
+ edac_dbg(0, " nr_pages/channel= %u channel-count = %d\n",
+ nr_pages, pvt->channel_count);
return nr_pages;
}
@@ -2205,6 +2194,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
static int init_csrows(struct mem_ctl_info *mci)
{
struct csrow_info *csrow;
+ struct dimm_info *dimm;
struct amd64_pvt *pvt = mci->pvt_info;
u64 base, mask;
u32 val;
@@ -2217,22 +2207,19 @@ static int init_csrows(struct mem_ctl_info *mci)
pvt->nbcfg = val;
- debugf0("node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
- pvt->mc_node_id, val,
- !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
+ edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n",
+ pvt->mc_node_id, val,
+ !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE));
for_each_chip_select(i, 0, pvt) {
- csrow = &mci->csrows[i];
+ csrow = mci->csrows[i];
if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) {
- debugf1("----CSROW %d EMPTY for node %d\n", i,
- pvt->mc_node_id);
+ edac_dbg(1, "----CSROW %d VALID for MC node %d\n",
+ i, pvt->mc_node_id);
continue;
}
- debugf1("----CSROW %d VALID for MC node %d\n",
- i, pvt->mc_node_id);
-
empty = 0;
if (csrow_enabled(i, 0, pvt))
nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
@@ -2244,8 +2231,9 @@ static int init_csrows(struct mem_ctl_info *mci)
mtype = amd64_determine_memory_type(pvt, i);
- debugf1(" for MC node %d csrow %d:\n", pvt->mc_node_id, i);
- debugf1(" nr_pages: %u\n", nr_pages * pvt->channel_count);
+ edac_dbg(1, " for MC node %d csrow %d:\n", pvt->mc_node_id, i);
+ edac_dbg(1, " nr_pages: %u\n",
+ nr_pages * pvt->channel_count);
/*
* determine whether CHIPKILL or JUST ECC or NO ECC is operating
@@ -2257,9 +2245,10 @@ static int init_csrows(struct mem_ctl_info *mci)
edac_mode = EDAC_NONE;
for (j = 0; j < pvt->channel_count; j++) {
- csrow->channels[j].dimm->mtype = mtype;
- csrow->channels[j].dimm->edac_mode = edac_mode;
- csrow->channels[j].dimm->nr_pages = nr_pages;
+ dimm = csrow->channels[j]->dimm;
+ dimm->mtype = mtype;
+ dimm->edac_mode = edac_mode;
+ dimm->nr_pages = nr_pages;
}
}
@@ -2296,9 +2285,9 @@ static bool amd64_nb_mce_bank_enabled_on_node(unsigned nid)
struct msr *reg = per_cpu_ptr(msrs, cpu);
nbe = reg->l & MSR_MCGCTL_NBE;
- debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
- cpu, reg->q,
- (nbe ? "enabled" : "disabled"));
+ edac_dbg(0, "core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
+ cpu, reg->q,
+ (nbe ? "enabled" : "disabled"));
if (!nbe)
goto out;
@@ -2369,8 +2358,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
amd64_read_pci_cfg(F3, NBCFG, &value);
- debugf0("1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
- nid, value, !!(value & NBCFG_ECC_ENABLE));
+ edac_dbg(0, "1: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
+ nid, value, !!(value & NBCFG_ECC_ENABLE));
if (!(value & NBCFG_ECC_ENABLE)) {
amd64_warn("DRAM ECC disabled on this node, enabling...\n");
@@ -2394,8 +2383,8 @@ static bool enable_ecc_error_reporting(struct ecc_settings *s, u8 nid,
s->flags.nb_ecc_prev = 1;
}
- debugf0("2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
- nid, value, !!(value & NBCFG_ECC_ENABLE));
+ edac_dbg(0, "2: node %d, NBCFG=0x%08x[DramEccEn: %d]\n",
+ nid, value, !!(value & NBCFG_ECC_ENABLE));
return ret;
}
@@ -2463,26 +2452,29 @@ static bool ecc_enabled(struct pci_dev *F3, u8 nid)
return true;
}
-struct mcidev_sysfs_attribute sysfs_attrs[ARRAY_SIZE(amd64_dbg_attrs) +
- ARRAY_SIZE(amd64_inj_attrs) +
- 1];
-
-struct mcidev_sysfs_attribute terminator = { .attr = { .name = NULL } };
-
-static void set_mc_sysfs_attrs(struct mem_ctl_info *mci)
+static int set_mc_sysfs_attrs(struct mem_ctl_info *mci)
{
- unsigned int i = 0, j = 0;
+ int rc;
- for (; i < ARRAY_SIZE(amd64_dbg_attrs); i++)
- sysfs_attrs[i] = amd64_dbg_attrs[i];
+ rc = amd64_create_sysfs_dbg_files(mci);
+ if (rc < 0)
+ return rc;
- if (boot_cpu_data.x86 >= 0x10)
- for (j = 0; j < ARRAY_SIZE(amd64_inj_attrs); j++, i++)
- sysfs_attrs[i] = amd64_inj_attrs[j];
+ if (boot_cpu_data.x86 >= 0x10) {
+ rc = amd64_create_sysfs_inject_files(mci);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
- sysfs_attrs[i] = terminator;
+static void del_mc_sysfs_attrs(struct mem_ctl_info *mci)
+{
+ amd64_remove_sysfs_dbg_files(mci);
- mci->mc_driver_sysfs_attributes = sysfs_attrs;
+ if (boot_cpu_data.x86 >= 0x10)
+ amd64_remove_sysfs_inject_files(mci);
}
static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
@@ -2601,20 +2593,22 @@ static int amd64_init_one_instance(struct pci_dev *F2)
goto err_siblings;
mci->pvt_info = pvt;
- mci->dev = &pvt->F2->dev;
+ mci->pdev = &pvt->F2->dev;
setup_mci_misc_attrs(mci, fam_type);
if (init_csrows(mci))
mci->edac_cap = EDAC_FLAG_NONE;
- set_mc_sysfs_attrs(mci);
-
ret = -ENODEV;
if (edac_mc_add_mc(mci)) {
- debugf1("failed edac_mc_add_mc()\n");
+ edac_dbg(1, "failed edac_mc_add_mc()\n");
goto err_add_mc;
}
+ if (set_mc_sysfs_attrs(mci)) {
+ edac_dbg(1, "failed edac_mc_add_mc()\n");
+ goto err_add_sysfs;
+ }
/* register stuff with EDAC MCE */
if (report_gart_errors)
@@ -2628,6 +2622,8 @@ static int amd64_init_one_instance(struct pci_dev *F2)
return 0;
+err_add_sysfs:
+ edac_mc_del_mc(mci->pdev);
err_add_mc:
edac_mc_free(mci);
@@ -2651,7 +2647,7 @@ static int __devinit amd64_probe_one_instance(struct pci_dev *pdev,
ret = pci_enable_device(pdev);
if (ret < 0) {
- debugf0("ret=%d\n", ret);
+ edac_dbg(0, "ret=%d\n", ret);
return -EIO;
}
@@ -2698,6 +2694,8 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
struct ecc_settings *s = ecc_stngs[nid];
+ mci = find_mci_by_dev(&pdev->dev);
+ del_mc_sysfs_attrs(mci);
/* Remove from EDAC CORE tracking list */
mci = edac_mc_del_mc(&pdev->dev);
if (!mci)
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index 9a666cb985b2..8d4804732bac 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -413,20 +413,33 @@ struct ecc_settings {
};
#ifdef CONFIG_EDAC_DEBUG
-#define NUM_DBG_ATTRS 5
+int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci);
+void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci);
+
#else
-#define NUM_DBG_ATTRS 0
+static inline int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+ return 0;
+}
+static void inline amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+}
#endif
#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION
-#define NUM_INJ_ATTRS 5
+int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci);
+void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci);
+
#else
-#define NUM_INJ_ATTRS 0
+static inline int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+ return 0;
+}
+static inline void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+}
#endif
-extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS],
- amd64_inj_attrs[NUM_INJ_ATTRS];
-
/*
* Each of the PCI Device IDs types have their own set of hardware accessor
* functions and per device encoding/decoding logic.
@@ -460,3 +473,5 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
u64 *hole_offset, u64 *hole_size);
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c
index e3562288f4ce..2c1bbf740605 100644
--- a/drivers/edac/amd64_edac_dbg.c
+++ b/drivers/edac/amd64_edac_dbg.c
@@ -1,8 +1,11 @@
#include "amd64_edac.h"
#define EDAC_DCT_ATTR_SHOW(reg) \
-static ssize_t amd64_##reg##_show(struct mem_ctl_info *mci, char *data) \
+static ssize_t amd64_##reg##_show(struct device *dev, \
+ struct device_attribute *mattr, \
+ char *data) \
{ \
+ struct mem_ctl_info *mci = to_mci(dev); \
struct amd64_pvt *pvt = mci->pvt_info; \
return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \
}
@@ -12,8 +15,12 @@ EDAC_DCT_ATTR_SHOW(dbam0);
EDAC_DCT_ATTR_SHOW(top_mem);
EDAC_DCT_ATTR_SHOW(top_mem2);
-static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data)
+static ssize_t amd64_hole_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
u64 hole_base = 0;
u64 hole_offset = 0;
u64 hole_size = 0;
@@ -27,46 +34,40 @@ static ssize_t amd64_hole_show(struct mem_ctl_info *mci, char *data)
/*
* update NUM_DBG_ATTRS in case you add new members
*/
-struct mcidev_sysfs_attribute amd64_dbg_attrs[] = {
+static DEVICE_ATTR(dhar, S_IRUGO, amd64_dhar_show, NULL);
+static DEVICE_ATTR(dbam, S_IRUGO, amd64_dbam0_show, NULL);
+static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL);
+static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL);
+static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL);
+
+int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+ int rc;
+
+ rc = device_create_file(&mci->dev, &dev_attr_dhar);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_dbam);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_topmem);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_topmem2);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_dram_hole);
+ if (rc < 0)
+ return rc;
- {
- .attr = {
- .name = "dhar",
- .mode = (S_IRUGO)
- },
- .show = amd64_dhar_show,
- .store = NULL,
- },
- {
- .attr = {
- .name = "dbam",
- .mode = (S_IRUGO)
- },
- .show = amd64_dbam0_show,
- .store = NULL,
- },
- {
- .attr = {
- .name = "topmem",
- .mode = (S_IRUGO)
- },
- .show = amd64_top_mem_show,
- .store = NULL,
- },
- {
- .attr = {
- .name = "topmem2",
- .mode = (S_IRUGO)
- },
- .show = amd64_top_mem2_show,
- .store = NULL,
- },
- {
- .attr = {
- .name = "dram_hole",
- .mode = (S_IRUGO)
- },
- .show = amd64_hole_show,
- .store = NULL,
- },
-};
+ return 0;
+}
+
+void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci)
+{
+ device_remove_file(&mci->dev, &dev_attr_dhar);
+ device_remove_file(&mci->dev, &dev_attr_dbam);
+ device_remove_file(&mci->dev, &dev_attr_topmem);
+ device_remove_file(&mci->dev, &dev_attr_topmem2);
+ device_remove_file(&mci->dev, &dev_attr_dram_hole);
+}
diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c
index 303f10e03dda..53d972e00dfb 100644
--- a/drivers/edac/amd64_edac_inj.c
+++ b/drivers/edac/amd64_edac_inj.c
@@ -1,7 +1,10 @@
#include "amd64_edac.h"
-static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_section_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *buf)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.section);
}
@@ -12,9 +15,11 @@ static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf)
*
* range: 0..3
*/
-static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci,
+static ssize_t amd64_inject_section_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret = 0;
@@ -33,8 +38,11 @@ static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci,
return ret;
}
-static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_word_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *buf)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.word);
}
@@ -45,9 +53,11 @@ static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf)
*
* range: 0..8
*/
-static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t amd64_inject_word_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret = 0;
@@ -66,8 +76,11 @@ static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci,
return ret;
}
-static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf)
+static ssize_t amd64_inject_ecc_vector_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *buf)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
return sprintf(buf, "0x%x\n", pvt->injection.bit_map);
}
@@ -77,9 +90,11 @@ static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf)
* corresponding bit within the error injection word above. When used during a
* DRAM ECC read, it holds the contents of the of the DRAM ECC bits.
*/
-static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t amd64_inject_ecc_vector_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
int ret = 0;
@@ -103,9 +118,11 @@ static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci,
* Do a DRAM ECC read. Assemble staged values in the pvt area, format into
* fields needed by the injection registers and read the NB Array Data Port.
*/
-static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t amd64_inject_read_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
u32 section, word_bits;
@@ -125,7 +142,8 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
- debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+ edac_dbg(0, "section=0x%x word_bits=0x%x\n",
+ section, word_bits);
return count;
}
@@ -136,9 +154,11 @@ static ssize_t amd64_inject_read_store(struct mem_ctl_info *mci,
* Do a DRAM ECC write. Assemble staged values in the pvt area and format into
* fields needed by the injection registers.
*/
-static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
+static ssize_t amd64_inject_write_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct amd64_pvt *pvt = mci->pvt_info;
unsigned long value;
u32 section, word_bits;
@@ -158,7 +178,8 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
/* Issue 'word' and 'bit' along with the READ request */
amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits);
- debugf0("section=0x%x word_bits=0x%x\n", section, word_bits);
+ edac_dbg(0, "section=0x%x word_bits=0x%x\n",
+ section, word_bits);
return count;
}
@@ -168,46 +189,47 @@ static ssize_t amd64_inject_write_store(struct mem_ctl_info *mci,
/*
* update NUM_INJ_ATTRS in case you add new members
*/
-struct mcidev_sysfs_attribute amd64_inj_attrs[] = {
-
- {
- .attr = {
- .name = "inject_section",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = amd64_inject_section_show,
- .store = amd64_inject_section_store,
- },
- {
- .attr = {
- .name = "inject_word",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = amd64_inject_word_show,
- .store = amd64_inject_word_store,
- },
- {
- .attr = {
- .name = "inject_ecc_vector",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = amd64_inject_ecc_vector_show,
- .store = amd64_inject_ecc_vector_store,
- },
- {
- .attr = {
- .name = "inject_write",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = NULL,
- .store = amd64_inject_write_store,
- },
- {
- .attr = {
- .name = "inject_read",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = NULL,
- .store = amd64_inject_read_store,
- },
-};
+
+static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
+ amd64_inject_section_show, amd64_inject_section_store);
+static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR,
+ amd64_inject_word_show, amd64_inject_word_store);
+static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR,
+ amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store);
+static DEVICE_ATTR(inject_write, S_IRUGO | S_IWUSR,
+ NULL, amd64_inject_write_store);
+static DEVICE_ATTR(inject_read, S_IRUGO | S_IWUSR,
+ NULL, amd64_inject_read_store);
+
+
+int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+ int rc;
+
+ rc = device_create_file(&mci->dev, &dev_attr_inject_section);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_word);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_ecc_vector);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_write);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_read);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci)
+{
+ device_remove_file(&mci->dev, &dev_attr_inject_section);
+ device_remove_file(&mci->dev, &dev_attr_inject_word);
+ device_remove_file(&mci->dev, &dev_attr_inject_ecc_vector);
+ device_remove_file(&mci->dev, &dev_attr_inject_write);
+ device_remove_file(&mci->dev, &dev_attr_inject_read);
+}
diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c
index 9774d443fa57..29eeb68a200c 100644
--- a/drivers/edac/amd76x_edac.c
+++ b/drivers/edac/amd76x_edac.c
@@ -105,7 +105,7 @@ static void amd76x_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS,
&info->ecc_mode_status);
@@ -145,10 +145,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
if (handle_errors) {
row = (info->ecc_mode_status >> 4) & 0xf;
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
- mci->csrows[row].first_page, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+ mci->csrows[row]->first_page, 0, 0,
row, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
}
@@ -160,10 +160,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
if (handle_errors) {
row = info->ecc_mode_status & 0xf;
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
- mci->csrows[row].first_page, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+ mci->csrows[row]->first_page, 0, 0,
row, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
}
@@ -180,7 +180,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
static void amd76x_check(struct mem_ctl_info *mci)
{
struct amd76x_error_info info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
amd76x_get_error_info(mci, &info);
amd76x_process_error_info(mci, &info, 1);
}
@@ -194,8 +194,8 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
int index;
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
/* find the DRAM Chip Select Base address and mask */
pci_read_config_dword(pdev,
@@ -241,7 +241,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
u32 ems_mode;
struct amd76x_error_info discard;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems);
ems_mode = (ems >> 10) & 0x3;
@@ -256,8 +256,8 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf0("%s(): mci = %p\n", __func__, mci);
- mci->dev = &pdev->dev;
+ edac_dbg(0, "mci = %p\n", mci);
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_RDDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
mci->edac_cap = ems_mode ?
@@ -276,7 +276,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail;
}
@@ -292,7 +292,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail:
@@ -304,7 +304,7 @@ fail:
static int __devinit amd76x_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* don't need to call pci_enable_device() */
return amd76x_probe1(pdev, ent->driver_data);
@@ -322,7 +322,7 @@ static void __devexit amd76x_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (amd76x_pci)
edac_pci_release_generic_ctl(amd76x_pci);
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
index 69ee6aab5c71..a1bbd8edd257 100644
--- a/drivers/edac/cell_edac.c
+++ b/drivers/edac/cell_edac.c
@@ -33,10 +33,10 @@ struct cell_edac_priv
static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
{
struct cell_edac_priv *priv = mci->pvt_info;
- struct csrow_info *csrow = &mci->csrows[0];
+ struct csrow_info *csrow = mci->csrows[0];
unsigned long address, pfn, offset, syndrome;
- dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n",
+ dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n",
priv->node, chan, ar);
/* Address decoding is likely a bit bogus, to dbl check */
@@ -48,18 +48,18 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
syndrome = (ar & 0x000000001fe00000ul) >> 21;
/* TODO: Decoding of the error address */
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
csrow->first_page + pfn, offset, syndrome,
- 0, chan, -1, "", "", NULL);
+ 0, chan, -1, "", "");
}
static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
{
struct cell_edac_priv *priv = mci->pvt_info;
- struct csrow_info *csrow = &mci->csrows[0];
+ struct csrow_info *csrow = mci->csrows[0];
unsigned long address, pfn, offset;
- dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n",
+ dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n",
priv->node, chan, ar);
/* Address decoding is likely a bit bogus, to dbl check */
@@ -70,9 +70,9 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
offset = address & ~PAGE_MASK;
/* TODO: Decoding of the error address */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
csrow->first_page + pfn, offset, 0,
- 0, chan, -1, "", "", NULL);
+ 0, chan, -1, "", "");
}
static void cell_edac_check(struct mem_ctl_info *mci)
@@ -83,7 +83,7 @@ static void cell_edac_check(struct mem_ctl_info *mci)
fir = in_be64(&priv->regs->mic_fir);
#ifdef DEBUG
if (fir != priv->prev_fir) {
- dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir);
+ dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir);
priv->prev_fir = fir;
}
#endif
@@ -119,14 +119,14 @@ static void cell_edac_check(struct mem_ctl_info *mci)
mb(); /* sync up */
#ifdef DEBUG
fir = in_be64(&priv->regs->mic_fir);
- dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir);
+ dev_dbg(mci->pdev, "fir clear : 0x%016lx\n", fir);
#endif
}
}
static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
{
- struct csrow_info *csrow = &mci->csrows[0];
+ struct csrow_info *csrow = mci->csrows[0];
struct dimm_info *dimm;
struct cell_edac_priv *priv = mci->pvt_info;
struct device_node *np;
@@ -150,12 +150,12 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
csrow->last_page = csrow->first_page + nr_pages - 1;
for (j = 0; j < csrow->nr_channels; j++) {
- dimm = csrow->channels[j].dimm;
+ dimm = csrow->channels[j]->dimm;
dimm->mtype = MEM_XDR;
dimm->edac_mode = EDAC_SECDED;
dimm->nr_pages = nr_pages / csrow->nr_channels;
}
- dev_dbg(mci->dev,
+ dev_dbg(mci->pdev,
"Initialized on node %d, chanmask=0x%x,"
" first_page=0x%lx, nr_pages=0x%x\n",
priv->node, priv->chanmask,
@@ -212,7 +212,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev)
priv->regs = regs;
priv->node = pdev->id;
priv->chanmask = chanmask;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_XDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
index e22030a9de66..c2ef13495873 100644
--- a/drivers/edac/cpc925_edac.c
+++ b/drivers/edac/cpc925_edac.c
@@ -316,13 +316,12 @@ static void get_total_mem(struct cpc925_mc_pdata *pdata)
reg += aw;
size = of_read_number(reg, sw);
reg += sw;
- debugf1("%s: start 0x%lx, size 0x%lx\n", __func__,
- start, size);
+ edac_dbg(1, "start 0x%lx, size 0x%lx\n", start, size);
pdata->total_mem += size;
} while (reg < reg_end);
of_node_put(np);
- debugf0("%s: total_mem 0x%lx\n", __func__, pdata->total_mem);
+ edac_dbg(0, "total_mem 0x%lx\n", pdata->total_mem);
}
static void cpc925_init_csrows(struct mem_ctl_info *mci)
@@ -330,8 +329,9 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
struct cpc925_mc_pdata *pdata = mci->pvt_info;
struct csrow_info *csrow;
struct dimm_info *dimm;
+ enum dev_type dtype;
int index, j;
- u32 mbmr, mbbar, bba;
+ u32 mbmr, mbbar, bba, grain;
unsigned long row_size, nr_pages, last_nr_pages = 0;
get_total_mem(pdata);
@@ -347,7 +347,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
if (bba == 0)
continue; /* not populated */
- csrow = &mci->csrows[index];
+ csrow = mci->csrows[index];
row_size = bba * (1UL << 28); /* 256M */
csrow->first_page = last_nr_pages;
@@ -355,37 +355,36 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
csrow->last_page = csrow->first_page + nr_pages - 1;
last_nr_pages = csrow->last_page + 1;
+ switch (csrow->nr_channels) {
+ case 1: /* Single channel */
+ grain = 32; /* four-beat burst of 32 bytes */
+ break;
+ case 2: /* Dual channel */
+ default:
+ grain = 64; /* four-beat burst of 64 bytes */
+ break;
+ }
+ switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
+ case 6: /* 0110, no way to differentiate X8 VS X16 */
+ case 5: /* 0101 */
+ case 8: /* 1000 */
+ dtype = DEV_X16;
+ break;
+ case 7: /* 0111 */
+ case 9: /* 1001 */
+ dtype = DEV_X8;
+ break;
+ default:
+ dtype = DEV_UNKNOWN;
+ break;
+ }
for (j = 0; j < csrow->nr_channels; j++) {
- dimm = csrow->channels[j].dimm;
-
+ dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / csrow->nr_channels;
dimm->mtype = MEM_RDDR;
dimm->edac_mode = EDAC_SECDED;
-
- switch (csrow->nr_channels) {
- case 1: /* Single channel */
- dimm->grain = 32; /* four-beat burst of 32 bytes */
- break;
- case 2: /* Dual channel */
- default:
- dimm->grain = 64; /* four-beat burst of 64 bytes */
- break;
- }
-
- switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
- case 6: /* 0110, no way to differentiate X8 VS X16 */
- case 5: /* 0101 */
- case 8: /* 1000 */
- dimm->dtype = DEV_X16;
- break;
- case 7: /* 0111 */
- case 9: /* 1001 */
- dimm->dtype = DEV_X8;
- break;
- default:
- dimm->dtype = DEV_UNKNOWN;
- break;
- }
+ dimm->grain = grain;
+ dimm->dtype = dtype;
}
}
}
@@ -463,7 +462,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
*csrow = rank;
#ifdef CONFIG_EDAC_DEBUG
- if (mci->csrows[rank].first_page == 0) {
+ if (mci->csrows[rank]->first_page == 0) {
cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a "
"non-populated csrow, broken hardware?\n");
return;
@@ -471,7 +470,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
#endif
/* Revert csrow number */
- pa = mci->csrows[rank].first_page << PAGE_SHIFT;
+ pa = mci->csrows[rank]->first_page << PAGE_SHIFT;
/* Revert column address */
col += bcnt;
@@ -512,7 +511,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear,
*offset = pa & (PAGE_SIZE - 1);
*pfn = pa >> PAGE_SHIFT;
- debugf0("%s: ECC physical address 0x%lx\n", __func__, pa);
+ edac_dbg(0, "ECC physical address 0x%lx\n", pa);
}
static int cpc925_mc_find_channel(struct mem_ctl_info *mci, u16 syndrome)
@@ -555,18 +554,18 @@ static void cpc925_mc_check(struct mem_ctl_info *mci)
if (apiexcp & CECC_EXCP_DETECTED) {
cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n");
channel = cpc925_mc_find_channel(mci, syndrome);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
pfn, offset, syndrome,
csrow, channel, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
if (apiexcp & UECC_EXCP_DETECTED) {
cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n");
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
pfn, offset, 0,
csrow, -1, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n");
@@ -852,8 +851,8 @@ static void cpc925_add_edac_devices(void __iomem *vbase)
goto err2;
}
- debugf0("%s: Successfully added edac device for %s\n",
- __func__, dev_info->ctl_name);
+ edac_dbg(0, "Successfully added edac device for %s\n",
+ dev_info->ctl_name);
continue;
@@ -884,8 +883,8 @@ static void cpc925_del_edac_devices(void)
if (dev_info->exit)
dev_info->exit(dev_info);
- debugf0("%s: Successfully deleted edac device for %s\n",
- __func__, dev_info->ctl_name);
+ edac_dbg(0, "Successfully deleted edac device for %s\n",
+ dev_info->ctl_name);
}
}
@@ -900,7 +899,7 @@ static int cpc925_get_sdram_scrub_rate(struct mem_ctl_info *mci)
mscr = __raw_readl(pdata->vbase + REG_MSCR_OFFSET);
si = (mscr & MSCR_SI_MASK) >> MSCR_SI_SHIFT;
- debugf0("%s, Mem Scrub Ctrl Register 0x%x\n", __func__, mscr);
+ edac_dbg(0, "Mem Scrub Ctrl Register 0x%x\n", mscr);
if (((mscr & MSCR_SCRUB_MOD_MASK) != MSCR_BACKGR_SCRUB) ||
(si == 0)) {
@@ -928,8 +927,7 @@ static int cpc925_mc_get_channels(void __iomem *vbase)
((mbcr & MBCR_64BITBUS_MASK) == 0))
dual = 1;
- debugf0("%s: %s channel\n", __func__,
- (dual > 0) ? "Dual" : "Single");
+ edac_dbg(0, "%s channel\n", (dual > 0) ? "Dual" : "Single");
return dual;
}
@@ -944,7 +942,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
struct resource *r;
int res = 0, nr_channels;
- debugf0("%s: %s platform device found!\n", __func__, pdev->name);
+ edac_dbg(0, "%s platform device found!\n", pdev->name);
if (!devres_open_group(&pdev->dev, cpc925_probe, GFP_KERNEL)) {
res = -ENOMEM;
@@ -995,7 +993,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
pdata->edac_idx = edac_mc_idx++;
pdata->name = pdev->name;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
platform_set_drvdata(pdev, mci);
mci->dev_name = dev_name(&pdev->dev);
mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
@@ -1026,7 +1024,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
cpc925_add_edac_devices(vbase);
/* get this far and it's successful */
- debugf0("%s: success\n", __func__);
+ edac_dbg(0, "success\n");
res = 0;
goto out;
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index 3186512c9739..a5ed6b795fd4 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -309,7 +309,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
u32 remap;
struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
if (page < pvt->tolm)
return page;
@@ -335,7 +335,7 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
int i;
struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* convert the addr to 4k page */
page = sec1_add >> (PAGE_SHIFT - 4);
@@ -371,10 +371,10 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
channel = !(error_one & 1);
/* e752x mc reads 34:6 of the DRAM linear address */
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offset_in_page(sec1_add << 4), sec1_syndrome,
row, channel, -1,
- "e752x CE", "", NULL);
+ "e752x CE", "");
}
static inline void process_ce(struct mem_ctl_info *mci, u16 error_one,
@@ -394,7 +394,7 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
int row;
struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
if (error_one & 0x0202) {
error_2b = ded_add;
@@ -408,11 +408,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
edac_mc_find_csrow_by_page(mci, block_page);
/* e752x mc reads 34:6 of the DRAM linear address */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
block_page,
offset_in_page(error_2b << 4), 0,
row, -1, -1,
- "e752x UE from Read", "", NULL);
+ "e752x UE from Read", "");
}
if (error_one & 0x0404) {
@@ -427,11 +427,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
edac_mc_find_csrow_by_page(mci, block_page);
/* e752x mc reads 34:6 of the DRAM linear address */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
block_page,
offset_in_page(error_2b << 4), 0,
row, -1, -1,
- "e752x UE from Scruber", "", NULL);
+ "e752x UE from Scruber", "");
}
}
@@ -453,10 +453,10 @@ static inline void process_ue_no_info_wr(struct mem_ctl_info *mci,
if (!handle_error)
return;
- debugf3("%s()\n", __func__);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+ edac_dbg(3, "\n");
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
-1, -1, -1,
- "e752x UE log memory write", "", NULL);
+ "e752x UE log memory write", "");
}
static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error,
@@ -982,7 +982,7 @@ static void e752x_check(struct mem_ctl_info *mci)
{
struct e752x_error_info info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
e752x_get_error_info(mci, &info);
e752x_process_error_info(mci, &info, 1);
}
@@ -1069,6 +1069,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
u16 ddrcsr)
{
struct csrow_info *csrow;
+ enum edac_type edac_mode;
unsigned long last_cumul_size;
int index, mem_dev, drc_chan;
int drc_drbg; /* DRB granularity 0=64mb, 1=128mb */
@@ -1095,14 +1096,13 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) {
/* mem_dev 0=x8, 1=x4 */
mem_dev = (dra >> (index * 4 + 2)) & 0x3;
- csrow = &mci->csrows[remap_csrow_index(mci, index)];
+ csrow = mci->csrows[remap_csrow_index(mci, index)];
mem_dev = (mem_dev == 2);
pci_read_config_byte(pdev, E752X_DRB + index, &value);
/* convert a 128 or 64 MiB DRB to a page size. */
cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
- cumul_size);
+ edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
if (cumul_size == last_cumul_size)
continue; /* not populated */
@@ -1111,29 +1111,29 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
nr_pages = cumul_size - last_cumul_size;
last_cumul_size = cumul_size;
+ /*
+ * if single channel or x8 devices then SECDED
+ * if dual channel and x4 then S4ECD4ED
+ */
+ if (drc_ddim) {
+ if (drc_chan && mem_dev) {
+ edac_mode = EDAC_S4ECD4ED;
+ mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+ } else {
+ edac_mode = EDAC_SECDED;
+ mci->edac_cap |= EDAC_FLAG_SECDED;
+ }
+ } else
+ edac_mode = EDAC_NONE;
for (i = 0; i < csrow->nr_channels; i++) {
- struct dimm_info *dimm = csrow->channels[i].dimm;
+ struct dimm_info *dimm = csrow->channels[i]->dimm;
- debugf3("Initializing rank at (%i,%i)\n", index, i);
+ edac_dbg(3, "Initializing rank at (%i,%i)\n", index, i);
dimm->nr_pages = nr_pages / csrow->nr_channels;
dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */
dimm->mtype = MEM_RDDR; /* only one type supported */
dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
- /*
- * if single channel or x8 devices then SECDED
- * if dual channel and x4 then S4ECD4ED
- */
- if (drc_ddim) {
- if (drc_chan && mem_dev) {
- dimm->edac_mode = EDAC_S4ECD4ED;
- mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
- } else {
- dimm->edac_mode = EDAC_SECDED;
- mci->edac_cap |= EDAC_FLAG_SECDED;
- }
- } else
- dimm->edac_mode = EDAC_NONE;
+ dimm->edac_mode = edac_mode;
}
}
}
@@ -1269,8 +1269,8 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
int drc_chan; /* Number of channels 0=1chan,1=2chan */
struct e752x_error_info discard;
- debugf0("%s(): mci\n", __func__);
- debugf0("Starting Probe1\n");
+ edac_dbg(0, "mci\n");
+ edac_dbg(0, "Starting Probe1\n");
/* check to see if device 0 function 1 is enabled; if it isn't, we
* assume the BIOS has reserved it for a reason and is expecting
@@ -1300,7 +1300,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf3("%s(): init mci\n", __func__);
+ edac_dbg(3, "init mci\n");
mci->mtype_cap = MEM_FLAG_RDDR;
/* 3100 IMCH supports SECDEC only */
mci->edac_ctl_cap = (dev_idx == I3100) ? EDAC_FLAG_SECDED :
@@ -1308,9 +1308,9 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
/* FIXME - what if different memory types are in different csrows? */
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = E752X_REVISION;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
- debugf3("%s(): init pvt\n", __func__);
+ edac_dbg(3, "init pvt\n");
pvt = (struct e752x_pvt *)mci->pvt_info;
pvt->dev_info = &e752x_devs[dev_idx];
pvt->mc_symmetric = ((ddrcsr & 0x10) != 0);
@@ -1320,7 +1320,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
return -ENODEV;
}
- debugf3("%s(): more mci init\n", __func__);
+ edac_dbg(3, "more mci init\n");
mci->ctl_name = pvt->dev_info->ctl_name;
mci->dev_name = pci_name(pdev);
mci->edac_check = e752x_check;
@@ -1342,7 +1342,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
mci->edac_cap = EDAC_FLAG_SECDED; /* the only mode supported */
else
mci->edac_cap |= EDAC_FLAG_NONE;
- debugf3("%s(): tolm, remapbase, remaplimit\n", __func__);
+ edac_dbg(3, "tolm, remapbase, remaplimit\n");
/* load the top of low memory, remap base, and remap limit vars */
pci_read_config_word(pdev, E752X_TOLM, &pci_data);
@@ -1359,7 +1359,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail;
}
@@ -1377,7 +1377,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail:
@@ -1393,7 +1393,7 @@ fail:
static int __devinit e752x_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* wake up and enable device */
if (pci_enable_device(pdev) < 0)
@@ -1407,7 +1407,7 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
struct e752x_pvt *pvt;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (e752x_pci)
edac_pci_release_generic_ctl(e752x_pci);
@@ -1453,7 +1453,7 @@ static int __init e752x_init(void)
{
int pci_rc;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -1464,7 +1464,7 @@ static int __init e752x_init(void)
static void __exit e752x_exit(void)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
pci_unregister_driver(&e752x_driver);
}
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c
index 9a9c1a546797..9ff57f361a43 100644
--- a/drivers/edac/e7xxx_edac.c
+++ b/drivers/edac/e7xxx_edac.c
@@ -166,7 +166,7 @@ static const struct e7xxx_dev_info e7xxx_devs[] = {
/* FIXME - is this valid for both SECDED and S4ECD4ED? */
static inline int e7xxx_find_channel(u16 syndrome)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
if ((syndrome & 0xff00) == 0)
return 0;
@@ -186,7 +186,7 @@ static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
u32 remap;
struct e7xxx_pvt *pvt = (struct e7xxx_pvt *)mci->pvt_info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
if ((page < pvt->tolm) ||
((page >= 0x100000) && (page < pvt->remapbase)))
@@ -208,7 +208,7 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
int row;
int channel;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* read the error address */
error_1b = info->dram_celog_add;
/* FIXME - should use PAGE_SHIFT */
@@ -219,15 +219,15 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
row = edac_mc_find_csrow_by_page(mci, page);
/* convert syndrome to channel */
channel = e7xxx_find_channel(syndrome);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, page, 0, syndrome,
- row, channel, -1, "e7xxx CE", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, page, 0, syndrome,
+ row, channel, -1, "e7xxx CE", "");
}
static void process_ce_no_info(struct mem_ctl_info *mci)
{
- debugf3("%s()\n", __func__);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
- "e7xxx CE log register overflow", "", NULL);
+ edac_dbg(3, "\n");
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
+ "e7xxx CE log register overflow", "");
}
static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
@@ -235,23 +235,23 @@ static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
u32 error_2b, block_page;
int row;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* read the error address */
error_2b = info->dram_uelog_add;
/* FIXME - should use PAGE_SHIFT */
block_page = error_2b >> 6; /* convert to 4k address */
row = edac_mc_find_csrow_by_page(mci, block_page);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, block_page, 0, 0,
- row, -1, -1, "e7xxx UE", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, block_page, 0, 0,
+ row, -1, -1, "e7xxx UE", "");
}
static void process_ue_no_info(struct mem_ctl_info *mci)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
- "e7xxx UE log register overflow", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
+ "e7xxx UE log register overflow", "");
}
static void e7xxx_get_error_info(struct mem_ctl_info *mci,
@@ -334,7 +334,7 @@ static void e7xxx_check(struct mem_ctl_info *mci)
{
struct e7xxx_error_info info;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
e7xxx_get_error_info(mci, &info);
e7xxx_process_error_info(mci, &info, 1);
}
@@ -362,6 +362,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
int drc_chan, drc_drbg, drc_ddim, mem_dev;
struct csrow_info *csrow;
struct dimm_info *dimm;
+ enum edac_type edac_mode;
pci_read_config_dword(pdev, E7XXX_DRA, &dra);
drc_chan = dual_channel_active(drc, dev_idx);
@@ -377,13 +378,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
for (index = 0; index < mci->nr_csrows; index++) {
/* mem_dev 0=x8, 1=x4 */
mem_dev = (dra >> (index * 4 + 3)) & 0x1;
- csrow = &mci->csrows[index];
+ csrow = mci->csrows[index];
pci_read_config_byte(pdev, E7XXX_DRB + index, &value);
/* convert a 64 or 32 MiB DRB to a page size. */
cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
- cumul_size);
+ edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
if (cumul_size == last_cumul_size)
continue; /* not populated */
@@ -392,28 +392,29 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
nr_pages = cumul_size - last_cumul_size;
last_cumul_size = cumul_size;
+ /*
+ * if single channel or x8 devices then SECDED
+ * if dual channel and x4 then S4ECD4ED
+ */
+ if (drc_ddim) {
+ if (drc_chan && mem_dev) {
+ edac_mode = EDAC_S4ECD4ED;
+ mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+ } else {
+ edac_mode = EDAC_SECDED;
+ mci->edac_cap |= EDAC_FLAG_SECDED;
+ }
+ } else
+ edac_mode = EDAC_NONE;
+
for (j = 0; j < drc_chan + 1; j++) {
- dimm = csrow->channels[j].dimm;
+ dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / (drc_chan + 1);
dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */
dimm->mtype = MEM_RDDR; /* only one type supported */
dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
- /*
- * if single channel or x8 devices then SECDED
- * if dual channel and x4 then S4ECD4ED
- */
- if (drc_ddim) {
- if (drc_chan && mem_dev) {
- dimm->edac_mode = EDAC_S4ECD4ED;
- mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
- } else {
- dimm->edac_mode = EDAC_SECDED;
- mci->edac_cap |= EDAC_FLAG_SECDED;
- }
- } else
- dimm->edac_mode = EDAC_NONE;
+ dimm->edac_mode = edac_mode;
}
}
}
@@ -428,7 +429,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
int drc_chan;
struct e7xxx_error_info discard;
- debugf0("%s(): mci\n", __func__);
+ edac_dbg(0, "mci\n");
pci_read_config_dword(pdev, E7XXX_DRC, &drc);
@@ -451,15 +452,15 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf3("%s(): init mci\n", __func__);
+ edac_dbg(3, "init mci\n");
mci->mtype_cap = MEM_FLAG_RDDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED |
EDAC_FLAG_S4ECD4ED;
/* FIXME - what if different memory types are in different csrows? */
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = E7XXX_REVISION;
- mci->dev = &pdev->dev;
- debugf3("%s(): init pvt\n", __func__);
+ mci->pdev = &pdev->dev;
+ edac_dbg(3, "init pvt\n");
pvt = (struct e7xxx_pvt *)mci->pvt_info;
pvt->dev_info = &e7xxx_devs[dev_idx];
pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
@@ -472,14 +473,14 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
goto fail0;
}
- debugf3("%s(): more mci init\n", __func__);
+ edac_dbg(3, "more mci init\n");
mci->ctl_name = pvt->dev_info->ctl_name;
mci->dev_name = pci_name(pdev);
mci->edac_check = e7xxx_check;
mci->ctl_page_to_phys = ctl_page_to_phys;
e7xxx_init_csrows(mci, pdev, dev_idx, drc);
mci->edac_cap |= EDAC_FLAG_NONE;
- debugf3("%s(): tolm, remapbase, remaplimit\n", __func__);
+ edac_dbg(3, "tolm, remapbase, remaplimit\n");
/* load the top of low memory, remap base, and remap limit vars */
pci_read_config_word(pdev, E7XXX_TOLM, &pci_data);
pvt->tolm = ((u32) pci_data) << 4;
@@ -498,7 +499,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail1;
}
@@ -514,7 +515,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail1:
@@ -530,7 +531,7 @@ fail0:
static int __devinit e7xxx_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* wake up and enable device */
return pci_enable_device(pdev) ?
@@ -542,7 +543,7 @@ static void __devexit e7xxx_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
struct e7xxx_pvt *pvt;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (e7xxx_pci)
edac_pci_release_generic_ctl(e7xxx_pci);
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 117490d4f835..23bb99fa44f1 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -71,26 +71,21 @@ extern const char *edac_mem_types[];
#ifdef CONFIG_EDAC_DEBUG
extern int edac_debug_level;
-#define edac_debug_printk(level, fmt, arg...) \
- do { \
- if (level <= edac_debug_level) \
- edac_printk(KERN_DEBUG, EDAC_DEBUG, \
- "%s: " fmt, __func__, ##arg); \
- } while (0)
-
-#define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ )
-#define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ )
-#define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ )
-#define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ )
-#define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ )
+#define edac_dbg(level, fmt, ...) \
+do { \
+ if (level <= edac_debug_level) \
+ edac_printk(KERN_DEBUG, EDAC_DEBUG, \
+ "%s: " fmt, __func__, ##__VA_ARGS__); \
+} while (0)
#else /* !CONFIG_EDAC_DEBUG */
-#define debugf0( ... )
-#define debugf1( ... )
-#define debugf2( ... )
-#define debugf3( ... )
-#define debugf4( ... )
+#define edac_dbg(level, fmt, ...) \
+do { \
+ if (0) \
+ edac_printk(KERN_DEBUG, EDAC_DEBUG, \
+ "%s: " fmt, __func__, ##__VA_ARGS__); \
+} while (0)
#endif /* !CONFIG_EDAC_DEBUG */
@@ -460,15 +455,15 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
unsigned long page);
void edac_mc_handle_error(const enum hw_event_mc_err_type type,
struct mem_ctl_info *mci,
+ const u16 error_count,
const unsigned long page_frame_number,
const unsigned long offset_in_page,
const unsigned long syndrome,
- const int layer0,
- const int layer1,
- const int layer2,
+ const int top_layer,
+ const int mid_layer,
+ const int low_layer,
const char *msg,
- const char *other_detail,
- const void *mcelog);
+ const char *other_detail);
/*
* edac_device APIs
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index ee3f1f810c1e..211021dfec73 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -40,12 +40,13 @@ static LIST_HEAD(edac_device_list);
#ifdef CONFIG_EDAC_DEBUG
static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev)
{
- debugf3("\tedac_dev = %p dev_idx=%d \n", edac_dev, edac_dev->dev_idx);
- debugf4("\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
- debugf3("\tdev = %p\n", edac_dev->dev);
- debugf3("\tmod_name:ctl_name = %s:%s\n",
- edac_dev->mod_name, edac_dev->ctl_name);
- debugf3("\tpvt_info = %p\n\n", edac_dev->pvt_info);
+ edac_dbg(3, "\tedac_dev = %p dev_idx=%d\n",
+ edac_dev, edac_dev->dev_idx);
+ edac_dbg(4, "\tedac_dev->edac_check = %p\n", edac_dev->edac_check);
+ edac_dbg(3, "\tdev = %p\n", edac_dev->dev);
+ edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
+ edac_dev->mod_name, edac_dev->ctl_name);
+ edac_dbg(3, "\tpvt_info = %p\n\n", edac_dev->pvt_info);
}
#endif /* CONFIG_EDAC_DEBUG */
@@ -82,8 +83,7 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
void *pvt, *p;
int err;
- debugf4("%s() instances=%d blocks=%d\n",
- __func__, nr_instances, nr_blocks);
+ edac_dbg(4, "instances=%d blocks=%d\n", nr_instances, nr_blocks);
/* Calculate the size of memory we need to allocate AND
* determine the offsets of the various item arrays
@@ -156,8 +156,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
/* Name of this edac device */
snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name);
- debugf4("%s() edac_dev=%p next after end=%p\n",
- __func__, dev_ctl, pvt + sz_private );
+ edac_dbg(4, "edac_dev=%p next after end=%p\n",
+ dev_ctl, pvt + sz_private);
/* Initialize every Instance */
for (instance = 0; instance < nr_instances; instance++) {
@@ -178,10 +178,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
snprintf(blk->name, sizeof(blk->name),
"%s%d", edac_block_name, block+offset_value);
- debugf4("%s() instance=%d inst_p=%p block=#%d "
- "block_p=%p name='%s'\n",
- __func__, instance, inst, block,
- blk, blk->name);
+ edac_dbg(4, "instance=%d inst_p=%p block=#%d block_p=%p name='%s'\n",
+ instance, inst, block, blk, blk->name);
/* if there are NO attributes OR no attribute pointer
* then continue on to next block iteration
@@ -194,8 +192,8 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
attrib_p = &dev_attrib[block*nr_instances*nr_attrib];
blk->block_attributes = attrib_p;
- debugf4("%s() THIS BLOCK_ATTRIB=%p\n",
- __func__, blk->block_attributes);
+ edac_dbg(4, "THIS BLOCK_ATTRIB=%p\n",
+ blk->block_attributes);
/* Initialize every user specified attribute in this
* block with the data the caller passed in
@@ -214,11 +212,10 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
attrib->block = blk; /* up link */
- debugf4("%s() alloc-attrib=%p attrib_name='%s' "
- "attrib-spec=%p spec-name=%s\n",
- __func__, attrib, attrib->attr.name,
- &attrib_spec[attr],
- attrib_spec[attr].attr.name
+ edac_dbg(4, "alloc-attrib=%p attrib_name='%s' attrib-spec=%p spec-name=%s\n",
+ attrib, attrib->attr.name,
+ &attrib_spec[attr],
+ attrib_spec[attr].attr.name
);
}
}
@@ -273,7 +270,7 @@ static struct edac_device_ctl_info *find_edac_device_by_dev(struct device *dev)
struct edac_device_ctl_info *edac_dev;
struct list_head *item;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
list_for_each(item, &edac_device_list) {
edac_dev = list_entry(item, struct edac_device_ctl_info, link);
@@ -408,7 +405,7 @@ static void edac_device_workq_function(struct work_struct *work_req)
void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
unsigned msec)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* take the arg 'msec' and set it into the control structure
* to used in the time period calculation
@@ -496,7 +493,7 @@ EXPORT_SYMBOL_GPL(edac_device_alloc_index);
*/
int edac_device_add_device(struct edac_device_ctl_info *edac_dev)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
#ifdef CONFIG_EDAC_DEBUG
if (edac_debug_level >= 3)
@@ -570,7 +567,7 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev)
{
struct edac_device_ctl_info *edac_dev;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mutex_lock(&device_ctls_mutex);
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c
index b4ea185ccebf..fb68a06ad683 100644
--- a/drivers/edac/edac_device_sysfs.c
+++ b/drivers/edac/edac_device_sysfs.c
@@ -202,7 +202,7 @@ static void edac_device_ctrl_master_release(struct kobject *kobj)
{
struct edac_device_ctl_info *edac_dev = to_edacdev(kobj);
- debugf4("%s() control index=%d\n", __func__, edac_dev->dev_idx);
+ edac_dbg(4, "control index=%d\n", edac_dev->dev_idx);
/* decrement the EDAC CORE module ref count */
module_put(edac_dev->owner);
@@ -233,12 +233,12 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
struct bus_type *edac_subsys;
int err;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
/* get the /sys/devices/system/edac reference */
edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
- debugf1("%s() no edac_subsys error\n", __func__);
+ edac_dbg(1, "no edac_subsys error\n");
err = -ENODEV;
goto err_out;
}
@@ -264,8 +264,8 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
&edac_subsys->dev_root->kobj,
"%s", edac_dev->name);
if (err) {
- debugf1("%s()Failed to register '.../edac/%s'\n",
- __func__, edac_dev->name);
+ edac_dbg(1, "Failed to register '.../edac/%s'\n",
+ edac_dev->name);
goto err_kobj_reg;
}
kobject_uevent(&edac_dev->kobj, KOBJ_ADD);
@@ -274,8 +274,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
* edac_device_unregister_sysfs_main_kobj() must be used
*/
- debugf4("%s() Registered '.../edac/%s' kobject\n",
- __func__, edac_dev->name);
+ edac_dbg(4, "Registered '.../edac/%s' kobject\n", edac_dev->name);
return 0;
@@ -296,9 +295,8 @@ err_out:
*/
void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
{
- debugf0("%s()\n", __func__);
- debugf4("%s() name of kobject is: %s\n",
- __func__, kobject_name(&dev->kobj));
+ edac_dbg(0, "\n");
+ edac_dbg(4, "name of kobject is: %s\n", kobject_name(&dev->kobj));
/*
* Unregister the edac device's kobject and
@@ -336,7 +334,7 @@ static void edac_device_ctrl_instance_release(struct kobject *kobj)
{
struct edac_device_instance *instance;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
/* map from this kobj to the main control struct
* and then dec the main kobj count
@@ -442,7 +440,7 @@ static void edac_device_ctrl_block_release(struct kobject *kobj)
{
struct edac_device_block *block;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
/* get the container of the kobj */
block = to_block(kobj);
@@ -524,10 +522,10 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
struct edac_dev_sysfs_block_attribute *sysfs_attrib;
struct kobject *main_kobj;
- debugf4("%s() Instance '%s' inst_p=%p block '%s' block_p=%p\n",
- __func__, instance->name, instance, block->name, block);
- debugf4("%s() block kobj=%p block kobj->parent=%p\n",
- __func__, &block->kobj, &block->kobj.parent);
+ edac_dbg(4, "Instance '%s' inst_p=%p block '%s' block_p=%p\n",
+ instance->name, instance, block->name, block);
+ edac_dbg(4, "block kobj=%p block kobj->parent=%p\n",
+ &block->kobj, &block->kobj.parent);
/* init this block's kobject */
memset(&block->kobj, 0, sizeof(struct kobject));
@@ -546,8 +544,7 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
&instance->kobj,
"%s", block->name);
if (err) {
- debugf1("%s() Failed to register instance '%s'\n",
- __func__, block->name);
+ edac_dbg(1, "Failed to register instance '%s'\n", block->name);
kobject_put(main_kobj);
err = -ENODEV;
goto err_out;
@@ -560,11 +557,9 @@ static int edac_device_create_block(struct edac_device_ctl_info *edac_dev,
if (sysfs_attrib && block->nr_attribs) {
for (i = 0; i < block->nr_attribs; i++, sysfs_attrib++) {
- debugf4("%s() creating block attrib='%s' "
- "attrib->%p to kobj=%p\n",
- __func__,
- sysfs_attrib->attr.name,
- sysfs_attrib, &block->kobj);
+ edac_dbg(4, "creating block attrib='%s' attrib->%p to kobj=%p\n",
+ sysfs_attrib->attr.name,
+ sysfs_attrib, &block->kobj);
/* Create each block_attribute file */
err = sysfs_create_file(&block->kobj,
@@ -647,14 +642,14 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
err = kobject_init_and_add(&instance->kobj, &ktype_instance_ctrl,
&edac_dev->kobj, "%s", instance->name);
if (err != 0) {
- debugf2("%s() Failed to register instance '%s'\n",
- __func__, instance->name);
+ edac_dbg(2, "Failed to register instance '%s'\n",
+ instance->name);
kobject_put(main_kobj);
goto err_out;
}
- debugf4("%s() now register '%d' blocks for instance %d\n",
- __func__, instance->nr_blocks, idx);
+ edac_dbg(4, "now register '%d' blocks for instance %d\n",
+ instance->nr_blocks, idx);
/* register all blocks of this instance */
for (i = 0; i < instance->nr_blocks; i++) {
@@ -670,8 +665,8 @@ static int edac_device_create_instance(struct edac_device_ctl_info *edac_dev,
}
kobject_uevent(&instance->kobj, KOBJ_ADD);
- debugf4("%s() Registered instance %d '%s' kobject\n",
- __func__, idx, instance->name);
+ edac_dbg(4, "Registered instance %d '%s' kobject\n",
+ idx, instance->name);
return 0;
@@ -715,7 +710,7 @@ static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev)
int i, j;
int err;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* iterate over creation of the instances */
for (i = 0; i < edac_dev->nr_instances; i++) {
@@ -817,12 +812,12 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
int err;
struct kobject *edac_kobj = &edac_dev->kobj;
- debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx);
+ edac_dbg(0, "idx=%d\n", edac_dev->dev_idx);
/* go create any main attributes callers wants */
err = edac_device_add_main_sysfs_attributes(edac_dev);
if (err) {
- debugf0("%s() failed to add sysfs attribs\n", __func__);
+ edac_dbg(0, "failed to add sysfs attribs\n");
goto err_out;
}
@@ -832,8 +827,7 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
err = sysfs_create_link(edac_kobj,
&edac_dev->dev->kobj, EDAC_DEVICE_SYMLINK);
if (err) {
- debugf0("%s() sysfs_create_link() returned err= %d\n",
- __func__, err);
+ edac_dbg(0, "sysfs_create_link() returned err= %d\n", err);
goto err_remove_main_attribs;
}
@@ -843,14 +837,13 @@ int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev)
*/
err = edac_device_create_instances(edac_dev);
if (err) {
- debugf0("%s() edac_device_create_instances() "
- "returned err= %d\n", __func__, err);
+ edac_dbg(0, "edac_device_create_instances() returned err= %d\n",
+ err);
goto err_remove_link;
}
- debugf4("%s() create-instances done, idx=%d\n",
- __func__, edac_dev->dev_idx);
+ edac_dbg(4, "create-instances done, idx=%d\n", edac_dev->dev_idx);
return 0;
@@ -873,7 +866,7 @@ err_out:
*/
void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* remove any main attributes for this device */
edac_device_remove_main_sysfs_attributes(edac_dev);
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index de5ba86e8b89..616d90bcb3a4 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -27,70 +27,95 @@
#include <linux/list.h>
#include <linux/ctype.h>
#include <linux/edac.h>
+#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/edac.h>
#include "edac_core.h"
#include "edac_module.h"
+#define CREATE_TRACE_POINTS
+#define TRACE_INCLUDE_PATH ../../include/ras
+#include <ras/ras_event.h>
+
/* lock to memory controller's control array */
static DEFINE_MUTEX(mem_ctls_mutex);
static LIST_HEAD(mc_devices);
+unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
+ unsigned len)
+{
+ struct mem_ctl_info *mci = dimm->mci;
+ int i, n, count = 0;
+ char *p = buf;
+
+ for (i = 0; i < mci->n_layers; i++) {
+ n = snprintf(p, len, "%s %d ",
+ edac_layer_name[mci->layers[i].type],
+ dimm->location[i]);
+ p += n;
+ len -= n;
+ count += n;
+ if (!len)
+ break;
+ }
+
+ return count;
+}
+
#ifdef CONFIG_EDAC_DEBUG
static void edac_mc_dump_channel(struct rank_info *chan)
{
- debugf4("\tchannel = %p\n", chan);
- debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
- debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
- debugf4("\tchannel->dimm = %p\n", chan->dimm);
+ edac_dbg(4, " channel->chan_idx = %d\n", chan->chan_idx);
+ edac_dbg(4, " channel = %p\n", chan);
+ edac_dbg(4, " channel->csrow = %p\n", chan->csrow);
+ edac_dbg(4, " channel->dimm = %p\n", chan->dimm);
}
-static void edac_mc_dump_dimm(struct dimm_info *dimm)
+static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)
{
- int i;
-
- debugf4("\tdimm = %p\n", dimm);
- debugf4("\tdimm->label = '%s'\n", dimm->label);
- debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
- debugf4("\tdimm location ");
- for (i = 0; i < dimm->mci->n_layers; i++) {
- printk(KERN_CONT "%d", dimm->location[i]);
- if (i < dimm->mci->n_layers - 1)
- printk(KERN_CONT ".");
- }
- printk(KERN_CONT "\n");
- debugf4("\tdimm->grain = %d\n", dimm->grain);
- debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
+ char location[80];
+
+ edac_dimm_info_location(dimm, location, sizeof(location));
+
+ edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
+ dimm->mci->mem_is_per_rank ? "rank" : "dimm",
+ number, location, dimm->csrow, dimm->cschannel);
+ edac_dbg(4, " dimm = %p\n", dimm);
+ edac_dbg(4, " dimm->label = '%s'\n", dimm->label);
+ edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
+ edac_dbg(4, " dimm->grain = %d\n", dimm->grain);
+ edac_dbg(4, " dimm->nr_pages = 0x%x\n", dimm->nr_pages);
}
static void edac_mc_dump_csrow(struct csrow_info *csrow)
{
- debugf4("\tcsrow = %p\n", csrow);
- debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
- debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
- debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
- debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
- debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
- debugf4("\tcsrow->channels = %p\n", csrow->channels);
- debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
+ edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
+ edac_dbg(4, " csrow = %p\n", csrow);
+ edac_dbg(4, " csrow->first_page = 0x%lx\n", csrow->first_page);
+ edac_dbg(4, " csrow->last_page = 0x%lx\n", csrow->last_page);
+ edac_dbg(4, " csrow->page_mask = 0x%lx\n", csrow->page_mask);
+ edac_dbg(4, " csrow->nr_channels = %d\n", csrow->nr_channels);
+ edac_dbg(4, " csrow->channels = %p\n", csrow->channels);
+ edac_dbg(4, " csrow->mci = %p\n", csrow->mci);
}
static void edac_mc_dump_mci(struct mem_ctl_info *mci)
{
- debugf3("\tmci = %p\n", mci);
- debugf3("\tmci->mtype_cap = %lx\n", mci->mtype_cap);
- debugf3("\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
- debugf3("\tmci->edac_cap = %lx\n", mci->edac_cap);
- debugf4("\tmci->edac_check = %p\n", mci->edac_check);
- debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
- mci->nr_csrows, mci->csrows);
- debugf3("\tmci->nr_dimms = %d, dimms = %p\n",
- mci->tot_dimms, mci->dimms);
- debugf3("\tdev = %p\n", mci->dev);
- debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name);
- debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
+ edac_dbg(3, "\tmci = %p\n", mci);
+ edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
+ edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
+ edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
+ edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
+ edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
+ mci->nr_csrows, mci->csrows);
+ edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
+ mci->tot_dimms, mci->dimms);
+ edac_dbg(3, "\tdev = %p\n", mci->pdev);
+ edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
+ mci->mod_name, mci->ctl_name);
+ edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
}
#endif /* CONFIG_EDAC_DEBUG */
@@ -205,15 +230,15 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
{
struct mem_ctl_info *mci;
struct edac_mc_layer *layer;
- struct csrow_info *csi, *csr;
- struct rank_info *chi, *chp, *chan;
+ struct csrow_info *csr;
+ struct rank_info *chan;
struct dimm_info *dimm;
u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
unsigned pos[EDAC_MAX_LAYERS];
unsigned size, tot_dimms = 1, count = 1;
unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
void *pvt, *p, *ptr = NULL;
- int i, j, err, row, chn, n, len;
+ int i, j, row, chn, n, len, off;
bool per_rank = false;
BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
@@ -239,26 +264,24 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
*/
mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
- csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows);
- chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels);
- dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms);
for (i = 0; i < n_layers; i++) {
count *= layers[i].size;
- debugf4("%s: errcount layer %d size %d\n", __func__, i, count);
+ edac_dbg(4, "errcount layer %d size %d\n", i, count);
ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
tot_errcount += 2 * count;
}
- debugf4("%s: allocating %d error counters\n", __func__, tot_errcount);
+ edac_dbg(4, "allocating %d error counters\n", tot_errcount);
pvt = edac_align_ptr(&ptr, sz_pvt, 1);
size = ((unsigned long)pvt) + sz_pvt;
- debugf1("%s(): allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
- __func__, size,
- tot_dimms,
- per_rank ? "ranks" : "dimms",
- tot_csrows * tot_channels);
+ edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
+ size,
+ tot_dimms,
+ per_rank ? "ranks" : "dimms",
+ tot_csrows * tot_channels);
+
mci = kzalloc(size, GFP_KERNEL);
if (mci == NULL)
return NULL;
@@ -267,9 +290,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
* rather than an imaginary chunk of memory located at address 0.
*/
layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
- csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
- chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
- dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm));
for (i = 0; i < n_layers; i++) {
mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
@@ -278,8 +298,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
/* setup index and various internal pointers */
mci->mc_idx = mc_num;
- mci->csrows = csi;
- mci->dimms = dimm;
mci->tot_dimms = tot_dimms;
mci->pvt_info = pvt;
mci->n_layers = n_layers;
@@ -290,40 +308,57 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
mci->mem_is_per_rank = per_rank;
/*
- * Fill the csrow struct
+ * Alocate and fill the csrow/channels structs
*/
+ mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL);
+ if (!mci->csrows)
+ goto error;
for (row = 0; row < tot_csrows; row++) {
- csr = &csi[row];
+ csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
+ if (!csr)
+ goto error;
+ mci->csrows[row] = csr;
csr->csrow_idx = row;
csr->mci = mci;
csr->nr_channels = tot_channels;
- chp = &chi[row * tot_channels];
- csr->channels = chp;
+ csr->channels = kcalloc(sizeof(*csr->channels), tot_channels,
+ GFP_KERNEL);
+ if (!csr->channels)
+ goto error;
for (chn = 0; chn < tot_channels; chn++) {
- chan = &chp[chn];
+ chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
+ if (!chan)
+ goto error;
+ csr->channels[chn] = chan;
chan->chan_idx = chn;
chan->csrow = csr;
}
}
/*
- * Fill the dimm struct
+ * Allocate and fill the dimm structs
*/
+ mci->dimms = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL);
+ if (!mci->dimms)
+ goto error;
+
memset(&pos, 0, sizeof(pos));
row = 0;
chn = 0;
- debugf4("%s: initializing %d %s\n", __func__, tot_dimms,
- per_rank ? "ranks" : "dimms");
for (i = 0; i < tot_dimms; i++) {
- chan = &csi[row].channels[chn];
- dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers,
- pos[0], pos[1], pos[2]);
- dimm->mci = mci;
+ chan = mci->csrows[row]->channels[chn];
+ off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]);
+ if (off < 0 || off >= tot_dimms) {
+ edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n");
+ goto error;
+ }
- debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__,
- i, per_rank ? "rank" : "dimm", (dimm - mci->dimms),
- pos[0], pos[1], pos[2], row, chn);
+ dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
+ if (!dimm)
+ goto error;
+ mci->dimms[off] = dimm;
+ dimm->mci = mci;
/*
* Copy DIMM location and initialize it.
@@ -367,16 +402,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
}
mci->op_state = OP_ALLOC;
- INIT_LIST_HEAD(&mci->grp_kobj_list);
-
- /*
- * Initialize the 'root' kobj for the edac_mc controller
- */
- err = edac_mc_register_sysfs_main_kobj(mci);
- if (err) {
- kfree(mci);
- return NULL;
- }
/* at this point, the root kobj is valid, and in order to
* 'free' the object, then the function:
@@ -384,7 +409,30 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
* which will perform kobj unregistration and the actual free
* will occur during the kobject callback operation
*/
+
return mci;
+
+error:
+ if (mci->dimms) {
+ for (i = 0; i < tot_dimms; i++)
+ kfree(mci->dimms[i]);
+ kfree(mci->dimms);
+ }
+ if (mci->csrows) {
+ for (chn = 0; chn < tot_channels; chn++) {
+ csr = mci->csrows[chn];
+ if (csr) {
+ for (chn = 0; chn < tot_channels; chn++)
+ kfree(csr->channels[chn]);
+ kfree(csr);
+ }
+ kfree(mci->csrows[i]);
+ }
+ kfree(mci->csrows);
+ }
+ kfree(mci);
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(edac_mc_alloc);
@@ -395,12 +443,10 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc);
*/
void edac_mc_free(struct mem_ctl_info *mci)
{
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
- edac_mc_unregister_sysfs_main_kobj(mci);
-
- /* free the mci instance memory here */
- kfree(mci);
+ /* the mci instance is freed here, when the sysfs object is dropped */
+ edac_unregister_sysfs(mci);
}
EXPORT_SYMBOL_GPL(edac_mc_free);
@@ -417,12 +463,12 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev)
struct mem_ctl_info *mci;
struct list_head *item;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
list_for_each(item, &mc_devices) {
mci = list_entry(item, struct mem_ctl_info, link);
- if (mci->dev == dev)
+ if (mci->pdev == dev)
return mci;
}
@@ -485,7 +531,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
*/
static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* if this instance is not in the POLL state, then simply return */
if (mci->op_state != OP_RUNNING_POLL)
@@ -512,8 +558,7 @@ static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
status = cancel_delayed_work(&mci->work);
if (status == 0) {
- debugf0("%s() not canceled, flush the queue\n",
- __func__);
+ edac_dbg(0, "not canceled, flush the queue\n");
/* workq instance might be running, wait for it */
flush_workqueue(edac_workqueue);
@@ -574,7 +619,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci)
insert_before = &mc_devices;
- p = find_mci_by_dev(mci->dev);
+ p = find_mci_by_dev(mci->pdev);
if (unlikely(p != NULL))
goto fail0;
@@ -596,7 +641,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci)
fail0:
edac_printk(KERN_WARNING, EDAC_MC,
- "%s (%s) %s %s already assigned %d\n", dev_name(p->dev),
+ "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
return 1;
@@ -660,7 +705,7 @@ EXPORT_SYMBOL(edac_mc_find);
/* FIXME - should a warning be printed if no error detection? correction? */
int edac_mc_add_mc(struct mem_ctl_info *mci)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
#ifdef CONFIG_EDAC_DEBUG
if (edac_debug_level >= 3)
@@ -670,15 +715,22 @@ int edac_mc_add_mc(struct mem_ctl_info *mci)
int i;
for (i = 0; i < mci->nr_csrows; i++) {
+ struct csrow_info *csrow = mci->csrows[i];
+ u32 nr_pages = 0;
int j;
- edac_mc_dump_csrow(&mci->csrows[i]);
- for (j = 0; j < mci->csrows[i].nr_channels; j++)
- edac_mc_dump_channel(&mci->csrows[i].
- channels[j]);
+ for (j = 0; j < csrow->nr_channels; j++)
+ nr_pages += csrow->channels[j]->dimm->nr_pages;
+ if (!nr_pages)
+ continue;
+ edac_mc_dump_csrow(csrow);
+ for (j = 0; j < csrow->nr_channels; j++)
+ if (csrow->channels[j]->dimm->nr_pages)
+ edac_mc_dump_channel(csrow->channels[j]);
}
for (i = 0; i < mci->tot_dimms; i++)
- edac_mc_dump_dimm(&mci->dimms[i]);
+ if (mci->dimms[i]->nr_pages)
+ edac_mc_dump_dimm(mci->dimms[i], i);
}
#endif
mutex_lock(&mem_ctls_mutex);
@@ -732,7 +784,7 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mutex_lock(&mem_ctls_mutex);
@@ -770,7 +822,7 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
void *virt_addr;
unsigned long flags = 0;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* ECC error page was not in our memory. Ignore it. */
if (!pfn_valid(page))
@@ -797,26 +849,26 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
/* FIXME - should return -1 */
int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
{
- struct csrow_info *csrows = mci->csrows;
+ struct csrow_info **csrows = mci->csrows;
int row, i, j, n;
- debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
+ edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
row = -1;
for (i = 0; i < mci->nr_csrows; i++) {
- struct csrow_info *csrow = &csrows[i];
+ struct csrow_info *csrow = csrows[i];
n = 0;
for (j = 0; j < csrow->nr_channels; j++) {
- struct dimm_info *dimm = csrow->channels[j].dimm;
+ struct dimm_info *dimm = csrow->channels[j]->dimm;
n += dimm->nr_pages;
}
if (n == 0)
continue;
- debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
- "mask(0x%lx)\n", mci->mc_idx, __func__,
- csrow->first_page, page, csrow->last_page,
- csrow->page_mask);
+ edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
+ mci->mc_idx,
+ csrow->first_page, page, csrow->last_page,
+ csrow->page_mask);
if ((page >= csrow->first_page) &&
(page <= csrow->last_page) &&
@@ -845,15 +897,16 @@ const char *edac_layer_name[] = {
EXPORT_SYMBOL_GPL(edac_layer_name);
static void edac_inc_ce_error(struct mem_ctl_info *mci,
- bool enable_per_layer_report,
- const int pos[EDAC_MAX_LAYERS])
+ bool enable_per_layer_report,
+ const int pos[EDAC_MAX_LAYERS],
+ const u16 count)
{
int i, index = 0;
- mci->ce_mc++;
+ mci->ce_mc += count;
if (!enable_per_layer_report) {
- mci->ce_noinfo_count++;
+ mci->ce_noinfo_count += count;
return;
}
@@ -861,7 +914,7 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci,
if (pos[i] < 0)
break;
index += pos[i];
- mci->ce_per_layer[i][index]++;
+ mci->ce_per_layer[i][index] += count;
if (i < mci->n_layers - 1)
index *= mci->layers[i + 1].size;
@@ -870,14 +923,15 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci,
static void edac_inc_ue_error(struct mem_ctl_info *mci,
bool enable_per_layer_report,
- const int pos[EDAC_MAX_LAYERS])
+ const int pos[EDAC_MAX_LAYERS],
+ const u16 count)
{
int i, index = 0;
- mci->ue_mc++;
+ mci->ue_mc += count;
if (!enable_per_layer_report) {
- mci->ce_noinfo_count++;
+ mci->ce_noinfo_count += count;
return;
}
@@ -885,7 +939,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci,
if (pos[i] < 0)
break;
index += pos[i];
- mci->ue_per_layer[i][index]++;
+ mci->ue_per_layer[i][index] += count;
if (i < mci->n_layers - 1)
index *= mci->layers[i + 1].size;
@@ -893,6 +947,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci,
}
static void edac_ce_error(struct mem_ctl_info *mci,
+ const u16 error_count,
const int pos[EDAC_MAX_LAYERS],
const char *msg,
const char *location,
@@ -902,23 +957,25 @@ static void edac_ce_error(struct mem_ctl_info *mci,
const bool enable_per_layer_report,
const unsigned long page_frame_number,
const unsigned long offset_in_page,
- u32 grain)
+ long grain)
{
unsigned long remapped_page;
if (edac_mc_get_log_ce()) {
if (other_detail && *other_detail)
edac_mc_printk(mci, KERN_WARNING,
- "CE %s on %s (%s%s - %s)\n",
+ "%d CE %s on %s (%s %s - %s)\n",
+ error_count,
msg, label, location,
detail, other_detail);
else
edac_mc_printk(mci, KERN_WARNING,
- "CE %s on %s (%s%s)\n",
+ "%d CE %s on %s (%s %s)\n",
+ error_count,
msg, label, location,
detail);
}
- edac_inc_ce_error(mci, enable_per_layer_report, pos);
+ edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count);
if (mci->scrub_mode & SCRUB_SW_SRC) {
/*
@@ -942,6 +999,7 @@ static void edac_ce_error(struct mem_ctl_info *mci,
}
static void edac_ue_error(struct mem_ctl_info *mci,
+ const u16 error_count,
const int pos[EDAC_MAX_LAYERS],
const char *msg,
const char *location,
@@ -953,12 +1011,14 @@ static void edac_ue_error(struct mem_ctl_info *mci,
if (edac_mc_get_log_ue()) {
if (other_detail && *other_detail)
edac_mc_printk(mci, KERN_WARNING,
- "UE %s on %s (%s%s - %s)\n",
+ "%d UE %s on %s (%s %s - %s)\n",
+ error_count,
msg, label, location, detail,
other_detail);
else
edac_mc_printk(mci, KERN_WARNING,
- "UE %s on %s (%s%s)\n",
+ "%d UE %s on %s (%s %s)\n",
+ error_count,
msg, label, location, detail);
}
@@ -971,33 +1031,53 @@ static void edac_ue_error(struct mem_ctl_info *mci,
msg, label, location, detail);
}
- edac_inc_ue_error(mci, enable_per_layer_report, pos);
+ edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count);
}
#define OTHER_LABEL " or "
+
+/**
+ * edac_mc_handle_error - reports a memory event to userspace
+ *
+ * @type: severity of the error (CE/UE/Fatal)
+ * @mci: a struct mem_ctl_info pointer
+ * @error_count: Number of errors of the same type
+ * @page_frame_number: mem page where the error occurred
+ * @offset_in_page: offset of the error inside the page
+ * @syndrome: ECC syndrome
+ * @top_layer: Memory layer[0] position
+ * @mid_layer: Memory layer[1] position
+ * @low_layer: Memory layer[2] position
+ * @msg: Message meaningful to the end users that
+ * explains the event
+ * @other_detail: Technical details about the event that
+ * may help hardware manufacturers and
+ * EDAC developers to analyse the event
+ */
void edac_mc_handle_error(const enum hw_event_mc_err_type type,
struct mem_ctl_info *mci,
+ const u16 error_count,
const unsigned long page_frame_number,
const unsigned long offset_in_page,
const unsigned long syndrome,
- const int layer0,
- const int layer1,
- const int layer2,
+ const int top_layer,
+ const int mid_layer,
+ const int low_layer,
const char *msg,
- const char *other_detail,
- const void *mcelog)
+ const char *other_detail)
{
/* FIXME: too much for stack: move it to some pre-alocated area */
char detail[80], location[80];
char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms];
char *p;
int row = -1, chan = -1;
- int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 };
+ int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
int i;
- u32 grain;
+ long grain;
bool enable_per_layer_report = false;
+ u8 grain_bits;
- debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(3, "MC%d\n", mci->mc_idx);
/*
* Check if the event report is consistent and if the memory
@@ -1043,13 +1123,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
p = label;
*p = '\0';
for (i = 0; i < mci->tot_dimms; i++) {
- struct dimm_info *dimm = &mci->dimms[i];
+ struct dimm_info *dimm = mci->dimms[i];
- if (layer0 >= 0 && layer0 != dimm->location[0])
+ if (top_layer >= 0 && top_layer != dimm->location[0])
continue;
- if (layer1 >= 0 && layer1 != dimm->location[1])
+ if (mid_layer >= 0 && mid_layer != dimm->location[1])
continue;
- if (layer2 >= 0 && layer2 != dimm->location[2])
+ if (low_layer >= 0 && low_layer != dimm->location[2])
continue;
/* get the max grain, over the error match range */
@@ -1075,11 +1155,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
* get csrow/channel of the DIMM, in order to allow
* incrementing the compat API counters
*/
- debugf4("%s: %s csrows map: (%d,%d)\n",
- __func__,
- mci->mem_is_per_rank ? "rank" : "dimm",
- dimm->csrow, dimm->cschannel);
-
+ edac_dbg(4, "%s csrows map: (%d,%d)\n",
+ mci->mem_is_per_rank ? "rank" : "dimm",
+ dimm->csrow, dimm->cschannel);
if (row == -1)
row = dimm->csrow;
else if (row >= 0 && row != dimm->csrow)
@@ -1095,19 +1173,18 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
if (!enable_per_layer_report) {
strcpy(label, "any memory");
} else {
- debugf4("%s: csrow/channel to increment: (%d,%d)\n",
- __func__, row, chan);
+ edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
if (p == label)
strcpy(label, "unknown memory");
if (type == HW_EVENT_ERR_CORRECTED) {
if (row >= 0) {
- mci->csrows[row].ce_count++;
+ mci->csrows[row]->ce_count += error_count;
if (chan >= 0)
- mci->csrows[row].channels[chan].ce_count++;
+ mci->csrows[row]->channels[chan]->ce_count += error_count;
}
} else
if (row >= 0)
- mci->csrows[row].ue_count++;
+ mci->csrows[row]->ue_count += error_count;
}
/* Fill the RAM location data */
@@ -1120,23 +1197,33 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
edac_layer_name[mci->layers[i].type],
pos[i]);
}
+ if (p > location)
+ *(p - 1) = '\0';
+
+ /* Report the error via the trace interface */
+
+ grain_bits = fls_long(grain) + 1;
+ trace_mc_event(type, msg, label, error_count,
+ mci->mc_idx, top_layer, mid_layer, low_layer,
+ PAGES_TO_MiB(page_frame_number) | offset_in_page,
+ grain_bits, syndrome, other_detail);
/* Memory type dependent details about the error */
if (type == HW_EVENT_ERR_CORRECTED) {
snprintf(detail, sizeof(detail),
- "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx",
+ "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx",
page_frame_number, offset_in_page,
grain, syndrome);
- edac_ce_error(mci, pos, msg, location, label, detail,
- other_detail, enable_per_layer_report,
+ edac_ce_error(mci, error_count, pos, msg, location, label,
+ detail, other_detail, enable_per_layer_report,
page_frame_number, offset_in_page, grain);
} else {
snprintf(detail, sizeof(detail),
- "page:0x%lx offset:0x%lx grain:%d",
+ "page:0x%lx offset:0x%lx grain:%ld",
page_frame_number, offset_in_page, grain);
- edac_ue_error(mci, pos, msg, location, label, detail,
- other_detail, enable_per_layer_report);
+ edac_ue_error(mci, error_count, pos, msg, location, label,
+ detail, other_detail, enable_per_layer_report);
}
}
EXPORT_SYMBOL_GPL(edac_mc_handle_error);
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index f6a29b0eedc8..ed0bc07b8503 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -7,17 +7,21 @@
*
* Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
*
+ * (c) 2012 - Mauro Carvalho Chehab <mchehab@redhat.com>
+ * The entire API were re-written, and ported to use struct device
+ *
*/
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/edac.h>
#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/uaccess.h>
#include "edac_core.h"
#include "edac_module.h"
-
/* MC EDAC Controls, setable by module parameter, and sysfs */
static int edac_mc_log_ue = 1;
static int edac_mc_log_ce = 1;
@@ -78,6 +82,8 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
&edac_mc_poll_msec, 0644);
MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
+static struct device *mci_pdev;
+
/*
* various constants for Memory Controllers
*/
@@ -125,317 +131,526 @@ static const char *edac_caps[] = {
[EDAC_S16ECD16ED] = "S16ECD16ED"
};
-/* EDAC sysfs CSROW data structures and methods
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+/*
+ * EDAC sysfs CSROW data structures and methods
+ */
+
+#define to_csrow(k) container_of(k, struct csrow_info, dev)
+
+/*
+ * We need it to avoid namespace conflicts between the legacy API
+ * and the per-dimm/per-rank one
*/
+#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
+ struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
+
+struct dev_ch_attribute {
+ struct device_attribute attr;
+ int channel;
+};
+
+#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
+ struct dev_ch_attribute dev_attr_legacy_##_name = \
+ { __ATTR(_name, _mode, _show, _store), (_var) }
+
+#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
/* Set of more default csrow<id> attribute show/store functions */
-static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_ue_count_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
+ struct csrow_info *csrow = to_csrow(dev);
+
return sprintf(data, "%u\n", csrow->ue_count);
}
-static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_ce_count_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
+ struct csrow_info *csrow = to_csrow(dev);
+
return sprintf(data, "%u\n", csrow->ce_count);
}
-static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_size_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
+ struct csrow_info *csrow = to_csrow(dev);
int i;
u32 nr_pages = 0;
for (i = 0; i < csrow->nr_channels; i++)
- nr_pages += csrow->channels[i].dimm->nr_pages;
-
+ nr_pages += csrow->channels[i]->dimm->nr_pages;
return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
}
-static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_mem_type_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
- return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]);
+ struct csrow_info *csrow = to_csrow(dev);
+
+ return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
}
-static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_dev_type_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
- return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]);
+ struct csrow_info *csrow = to_csrow(dev);
+
+ return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
}
-static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
- int private)
+static ssize_t csrow_edac_mode_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
- return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]);
+ struct csrow_info *csrow = to_csrow(dev);
+
+ return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
}
/* show/store functions for DIMM Label attributes */
-static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
- char *data, int channel)
+static ssize_t channel_dimm_label_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct csrow_info *csrow = to_csrow(dev);
+ unsigned chan = to_channel(mattr);
+ struct rank_info *rank = csrow->channels[chan];
+
/* if field has not been initialized, there is nothing to send */
- if (!csrow->channels[channel].dimm->label[0])
+ if (!rank->dimm->label[0])
return 0;
return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
- csrow->channels[channel].dimm->label);
+ rank->dimm->label);
}
-static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
- const char *data,
- size_t count, int channel)
+static ssize_t channel_dimm_label_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct csrow_info *csrow = to_csrow(dev);
+ unsigned chan = to_channel(mattr);
+ struct rank_info *rank = csrow->channels[chan];
+
ssize_t max_size = 0;
max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
- strncpy(csrow->channels[channel].dimm->label, data, max_size);
- csrow->channels[channel].dimm->label[max_size] = '\0';
+ strncpy(rank->dimm->label, data, max_size);
+ rank->dimm->label[max_size] = '\0';
return max_size;
}
/* show function for dynamic chX_ce_count attribute */
-static ssize_t channel_ce_count_show(struct csrow_info *csrow,
- char *data, int channel)
+static ssize_t channel_ce_count_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
{
- return sprintf(data, "%u\n", csrow->channels[channel].ce_count);
+ struct csrow_info *csrow = to_csrow(dev);
+ unsigned chan = to_channel(mattr);
+ struct rank_info *rank = csrow->channels[chan];
+
+ return sprintf(data, "%u\n", rank->ce_count);
}
-/* csrow specific attribute structure */
-struct csrowdev_attribute {
- struct attribute attr;
- ssize_t(*show) (struct csrow_info *, char *, int);
- ssize_t(*store) (struct csrow_info *, const char *, size_t, int);
- int private;
-};
+/* cwrow<id>/attribute files */
+DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
+DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
+DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
+DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
+DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
+DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
-#define to_csrow(k) container_of(k, struct csrow_info, kobj)
-#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr)
+/* default attributes of the CSROW<id> object */
+static struct attribute *csrow_attrs[] = {
+ &dev_attr_legacy_dev_type.attr,
+ &dev_attr_legacy_mem_type.attr,
+ &dev_attr_legacy_edac_mode.attr,
+ &dev_attr_legacy_size_mb.attr,
+ &dev_attr_legacy_ue_count.attr,
+ &dev_attr_legacy_ce_count.attr,
+ NULL,
+};
-/* Set of show/store higher level functions for default csrow attributes */
-static ssize_t csrowdev_show(struct kobject *kobj,
- struct attribute *attr, char *buffer)
-{
- struct csrow_info *csrow = to_csrow(kobj);
- struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
+static struct attribute_group csrow_attr_grp = {
+ .attrs = csrow_attrs,
+};
- if (csrowdev_attr->show)
- return csrowdev_attr->show(csrow,
- buffer, csrowdev_attr->private);
- return -EIO;
-}
+static const struct attribute_group *csrow_attr_groups[] = {
+ &csrow_attr_grp,
+ NULL
+};
-static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr,
- const char *buffer, size_t count)
+static void csrow_attr_release(struct device *dev)
{
- struct csrow_info *csrow = to_csrow(kobj);
- struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
-
- if (csrowdev_attr->store)
- return csrowdev_attr->store(csrow,
- buffer,
- count, csrowdev_attr->private);
- return -EIO;
-}
+ struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
-static const struct sysfs_ops csrowfs_ops = {
- .show = csrowdev_show,
- .store = csrowdev_store
-};
+ edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
+ kfree(csrow);
+}
-#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \
-static struct csrowdev_attribute attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .show = _show, \
- .store = _store, \
- .private = _private, \
+static struct device_type csrow_attr_type = {
+ .groups = csrow_attr_groups,
+ .release = csrow_attr_release,
};
-/* default cwrow<id>/attribute files */
-CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0);
-CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0);
-CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0);
-CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0);
-CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0);
-CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0);
+/*
+ * possible dynamic channel DIMM Label attribute files
+ *
+ */
-/* default attributes of the CSROW<id> object */
-static struct csrowdev_attribute *default_csrow_attr[] = {
- &attr_dev_type,
- &attr_mem_type,
- &attr_edac_mode,
- &attr_size_mb,
- &attr_ue_count,
- &attr_ce_count,
- NULL,
-};
+#define EDAC_NR_CHANNELS 6
-/* possible dynamic channel DIMM Label attribute files */
-CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 0);
-CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 1);
-CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 2);
-CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 3);
-CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 4);
-CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR,
+DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
channel_dimm_label_show, channel_dimm_label_store, 5);
/* Total possible dynamic DIMM Label attribute file table */
-static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = {
- &attr_ch0_dimm_label,
- &attr_ch1_dimm_label,
- &attr_ch2_dimm_label,
- &attr_ch3_dimm_label,
- &attr_ch4_dimm_label,
- &attr_ch5_dimm_label
+static struct device_attribute *dynamic_csrow_dimm_attr[] = {
+ &dev_attr_legacy_ch0_dimm_label.attr,
+ &dev_attr_legacy_ch1_dimm_label.attr,
+ &dev_attr_legacy_ch2_dimm_label.attr,
+ &dev_attr_legacy_ch3_dimm_label.attr,
+ &dev_attr_legacy_ch4_dimm_label.attr,
+ &dev_attr_legacy_ch5_dimm_label.attr
};
/* possible dynamic channel ce_count attribute files */
-CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0);
-CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1);
-CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2);
-CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3);
-CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4);
-CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5);
+DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 0);
+DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 1);
+DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 2);
+DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 3);
+DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 4);
+DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR,
+ channel_ce_count_show, NULL, 5);
/* Total possible dynamic ce_count attribute file table */
-static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = {
- &attr_ch0_ce_count,
- &attr_ch1_ce_count,
- &attr_ch2_ce_count,
- &attr_ch3_ce_count,
- &attr_ch4_ce_count,
- &attr_ch5_ce_count
+static struct device_attribute *dynamic_csrow_ce_count_attr[] = {
+ &dev_attr_legacy_ch0_ce_count.attr,
+ &dev_attr_legacy_ch1_ce_count.attr,
+ &dev_attr_legacy_ch2_ce_count.attr,
+ &dev_attr_legacy_ch3_ce_count.attr,
+ &dev_attr_legacy_ch4_ce_count.attr,
+ &dev_attr_legacy_ch5_ce_count.attr
};
-#define EDAC_NR_CHANNELS 6
+static inline int nr_pages_per_csrow(struct csrow_info *csrow)
+{
+ int chan, nr_pages = 0;
+
+ for (chan = 0; chan < csrow->nr_channels; chan++)
+ nr_pages += csrow->channels[chan]->dimm->nr_pages;
+
+ return nr_pages;
+}
-/* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */
-static int edac_create_channel_files(struct kobject *kobj, int chan)
+/* Create a CSROW object under specifed edac_mc_device */
+static int edac_create_csrow_object(struct mem_ctl_info *mci,
+ struct csrow_info *csrow, int index)
{
- int err = -ENODEV;
+ int err, chan;
+
+ if (csrow->nr_channels >= EDAC_NR_CHANNELS)
+ return -ENODEV;
+
+ csrow->dev.type = &csrow_attr_type;
+ csrow->dev.bus = &mci->bus;
+ device_initialize(&csrow->dev);
+ csrow->dev.parent = &mci->dev;
+ dev_set_name(&csrow->dev, "csrow%d", index);
+ dev_set_drvdata(&csrow->dev, csrow);
- if (chan >= EDAC_NR_CHANNELS)
+ edac_dbg(0, "creating (virtual) csrow node %s\n",
+ dev_name(&csrow->dev));
+
+ err = device_add(&csrow->dev);
+ if (err < 0)
return err;
- /* create the DIMM label attribute file */
- err = sysfs_create_file(kobj,
- (struct attribute *)
- dynamic_csrow_dimm_attr[chan]);
-
- if (!err) {
- /* create the CE Count attribute file */
- err = sysfs_create_file(kobj,
- (struct attribute *)
- dynamic_csrow_ce_count_attr[chan]);
- } else {
- debugf1("%s() dimm labels and ce_count files created",
- __func__);
+ for (chan = 0; chan < csrow->nr_channels; chan++) {
+ /* Only expose populated DIMMs */
+ if (!csrow->channels[chan]->dimm->nr_pages)
+ continue;
+ err = device_create_file(&csrow->dev,
+ dynamic_csrow_dimm_attr[chan]);
+ if (err < 0)
+ goto error;
+ err = device_create_file(&csrow->dev,
+ dynamic_csrow_ce_count_attr[chan]);
+ if (err < 0) {
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_dimm_attr[chan]);
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ for (--chan; chan >= 0; chan--) {
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_dimm_attr[chan]);
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_ce_count_attr[chan]);
}
+ put_device(&csrow->dev);
return err;
}
-/* No memory to release for this kobj */
-static void edac_csrow_instance_release(struct kobject *kobj)
+/* Create a CSROW object under specifed edac_mc_device */
+static int edac_create_csrow_objects(struct mem_ctl_info *mci)
{
- struct mem_ctl_info *mci;
- struct csrow_info *cs;
+ int err, i, chan;
+ struct csrow_info *csrow;
+
+ for (i = 0; i < mci->nr_csrows; i++) {
+ csrow = mci->csrows[i];
+ if (!nr_pages_per_csrow(csrow))
+ continue;
+ err = edac_create_csrow_object(mci, mci->csrows[i], i);
+ if (err < 0)
+ goto error;
+ }
+ return 0;
- debugf1("%s()\n", __func__);
+error:
+ for (--i; i >= 0; i--) {
+ csrow = mci->csrows[i];
+ if (!nr_pages_per_csrow(csrow))
+ continue;
+ for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
+ if (!csrow->channels[chan]->dimm->nr_pages)
+ continue;
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_dimm_attr[chan]);
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_ce_count_attr[chan]);
+ }
+ put_device(&mci->csrows[i]->dev);
+ }
- cs = container_of(kobj, struct csrow_info, kobj);
- mci = cs->mci;
+ return err;
+}
- kobject_put(&mci->edac_mci_kobj);
+static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
+{
+ int i, chan;
+ struct csrow_info *csrow;
+
+ for (i = mci->nr_csrows - 1; i >= 0; i--) {
+ csrow = mci->csrows[i];
+ if (!nr_pages_per_csrow(csrow))
+ continue;
+ for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
+ if (!csrow->channels[chan]->dimm->nr_pages)
+ continue;
+ edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n",
+ i, chan);
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_dimm_attr[chan]);
+ device_remove_file(&csrow->dev,
+ dynamic_csrow_ce_count_attr[chan]);
+ }
+ put_device(&mci->csrows[i]->dev);
+ device_del(&mci->csrows[i]->dev);
+ }
}
+#endif
-/* the kobj_type instance for a CSROW */
-static struct kobj_type ktype_csrow = {
- .release = edac_csrow_instance_release,
- .sysfs_ops = &csrowfs_ops,
- .default_attrs = (struct attribute **)default_csrow_attr,
+/*
+ * Per-dimm (or per-rank) devices
+ */
+
+#define to_dimm(k) container_of(k, struct dimm_info, dev)
+
+/* show/store functions for DIMM Label attributes */
+static ssize_t dimmdev_location_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ return edac_dimm_info_location(dimm, data, PAGE_SIZE);
+}
+
+static ssize_t dimmdev_label_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ /* if field has not been initialized, there is nothing to send */
+ if (!dimm->label[0])
+ return 0;
+
+ return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
+}
+
+static ssize_t dimmdev_label_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data,
+ size_t count)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ ssize_t max_size = 0;
+
+ max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
+ strncpy(dimm->label, data, max_size);
+ dimm->label[max_size] = '\0';
+
+ return max_size;
+}
+
+static ssize_t dimmdev_size_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
+}
+
+static ssize_t dimmdev_mem_type_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ return sprintf(data, "%s\n", mem_types[dimm->mtype]);
+}
+
+static ssize_t dimmdev_dev_type_show(struct device *dev,
+ struct device_attribute *mattr, char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ return sprintf(data, "%s\n", dev_types[dimm->dtype]);
+}
+
+static ssize_t dimmdev_edac_mode_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
+{
+ struct dimm_info *dimm = to_dimm(dev);
+
+ return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
+}
+
+/* dimm/rank attribute files */
+static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
+ dimmdev_label_show, dimmdev_label_store);
+static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
+static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
+static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
+static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
+static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
+
+/* attributes of the dimm<id>/rank<id> object */
+static struct attribute *dimm_attrs[] = {
+ &dev_attr_dimm_label.attr,
+ &dev_attr_dimm_location.attr,
+ &dev_attr_size.attr,
+ &dev_attr_dimm_mem_type.attr,
+ &dev_attr_dimm_dev_type.attr,
+ &dev_attr_dimm_edac_mode.attr,
+ NULL,
};
-/* Create a CSROW object under specifed edac_mc_device */
-static int edac_create_csrow_object(struct mem_ctl_info *mci,
- struct csrow_info *csrow, int index)
+static struct attribute_group dimm_attr_grp = {
+ .attrs = dimm_attrs,
+};
+
+static const struct attribute_group *dimm_attr_groups[] = {
+ &dimm_attr_grp,
+ NULL
+};
+
+static void dimm_attr_release(struct device *dev)
{
- struct kobject *kobj_mci = &mci->edac_mci_kobj;
- struct kobject *kobj;
- int chan;
- int err;
+ struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
- /* generate ..../edac/mc/mc<id>/csrow<index> */
- memset(&csrow->kobj, 0, sizeof(csrow->kobj));
- csrow->mci = mci; /* include container up link */
+ edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
+ kfree(dimm);
+}
- /* bump the mci instance's kobject's ref count */
- kobj = kobject_get(&mci->edac_mci_kobj);
- if (!kobj) {
- err = -ENODEV;
- goto err_out;
- }
+static struct device_type dimm_attr_type = {
+ .groups = dimm_attr_groups,
+ .release = dimm_attr_release,
+};
+
+/* Create a DIMM object under specifed memory controller device */
+static int edac_create_dimm_object(struct mem_ctl_info *mci,
+ struct dimm_info *dimm,
+ int index)
+{
+ int err;
+ dimm->mci = mci;
- /* Instanstiate the csrow object */
- err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci,
- "csrow%d", index);
- if (err)
- goto err_release_top_kobj;
+ dimm->dev.type = &dimm_attr_type;
+ dimm->dev.bus = &mci->bus;
+ device_initialize(&dimm->dev);
- /* At this point, to release a csrow kobj, one must
- * call the kobject_put and allow that tear down
- * to work the releasing
- */
+ dimm->dev.parent = &mci->dev;
+ if (mci->mem_is_per_rank)
+ dev_set_name(&dimm->dev, "rank%d", index);
+ else
+ dev_set_name(&dimm->dev, "dimm%d", index);
+ dev_set_drvdata(&dimm->dev, dimm);
+ pm_runtime_forbid(&mci->dev);
- /* Create the dyanmic attribute files on this csrow,
- * namely, the DIMM labels and the channel ce_count
- */
- for (chan = 0; chan < csrow->nr_channels; chan++) {
- err = edac_create_channel_files(&csrow->kobj, chan);
- if (err) {
- /* special case the unregister here */
- kobject_put(&csrow->kobj);
- goto err_out;
- }
- }
- kobject_uevent(&csrow->kobj, KOBJ_ADD);
- return 0;
+ err = device_add(&dimm->dev);
- /* error unwind stack */
-err_release_top_kobj:
- kobject_put(&mci->edac_mci_kobj);
+ edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
-err_out:
return err;
}
-/* default sysfs methods and data structures for the main MCI kobject */
+/*
+ * Memory controller device
+ */
+
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
-static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
+static ssize_t mci_reset_counters_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
- int row, chan;
-
- mci->ue_noinfo_count = 0;
- mci->ce_noinfo_count = 0;
+ struct mem_ctl_info *mci = to_mci(dev);
+ int cnt, row, chan, i;
mci->ue_mc = 0;
mci->ce_mc = 0;
+ mci->ue_noinfo_count = 0;
+ mci->ce_noinfo_count = 0;
for (row = 0; row < mci->nr_csrows; row++) {
- struct csrow_info *ri = &mci->csrows[row];
+ struct csrow_info *ri = mci->csrows[row];
ri->ue_count = 0;
ri->ce_count = 0;
for (chan = 0; chan < ri->nr_channels; chan++)
- ri->channels[chan].ce_count = 0;
+ ri->channels[chan]->ce_count = 0;
+ }
+
+ cnt = 1;
+ for (i = 0; i < mci->n_layers; i++) {
+ cnt *= mci->layers[i].size;
+ memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
+ memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
}
mci->start_time = jiffies;
@@ -451,9 +666,11 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
* Negative value still means that an error has occurred while setting
* the scrub rate.
*/
-static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
+static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
unsigned long bandwidth = 0;
int new_bw = 0;
@@ -476,8 +693,11 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
/*
* ->get_sdram_scrub_rate() return value semantics same as above.
*/
-static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
int bandwidth = 0;
if (!mci->get_sdram_scrub_rate)
@@ -493,45 +713,72 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
}
/* default attribute files for the MCI object */
-static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ue_count_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%d\n", mci->ue_mc);
}
-static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ce_count_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%d\n", mci->ce_mc);
}
-static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ce_noinfo_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%d\n", mci->ce_noinfo_count);
}
-static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ue_noinfo_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%d\n", mci->ue_noinfo_count);
}
-static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_seconds_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
}
-static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_ctl_name_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
+
return sprintf(data, "%s\n", mci->ctl_name);
}
-static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mci_size_mb_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
int total_pages = 0, csrow_idx, j;
for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
- struct csrow_info *csrow = &mci->csrows[csrow_idx];
+ struct csrow_info *csrow = mci->csrows[csrow_idx];
for (j = 0; j < csrow->nr_channels; j++) {
- struct dimm_info *dimm = csrow->channels[j].dimm;
+ struct dimm_info *dimm = csrow->channels[j]->dimm;
total_pages += dimm->nr_pages;
}
@@ -540,361 +787,187 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
}
-#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj)
-#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr)
-
-/* MCI show/store functions for top most object */
-static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr,
- char *buffer)
+static ssize_t mci_max_location_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
- struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
- struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
- debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
+ struct mem_ctl_info *mci = to_mci(dev);
+ int i;
+ char *p = data;
- if (mcidev_attr->show)
- return mcidev_attr->show(mem_ctl_info, buffer);
+ for (i = 0; i < mci->n_layers; i++) {
+ p += sprintf(p, "%s %d ",
+ edac_layer_name[mci->layers[i].type],
+ mci->layers[i].size - 1);
+ }
- return -EIO;
+ return p - data;
}
-static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
- const char *buffer, size_t count)
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t edac_fake_inject_write(struct file *file,
+ const char __user *data,
+ size_t count, loff_t *ppos)
{
- struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
- struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
- debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
- if (mcidev_attr->store)
- return mcidev_attr->store(mem_ctl_info, buffer, count);
+ struct device *dev = file->private_data;
+ struct mem_ctl_info *mci = to_mci(dev);
+ static enum hw_event_mc_err_type type;
+ u16 errcount = mci->fake_inject_count;
+
+ if (!errcount)
+ errcount = 1;
+
+ type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
+ : HW_EVENT_ERR_CORRECTED;
+
+ printk(KERN_DEBUG
+ "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
+ errcount,
+ (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
+ errcount > 1 ? "s" : "",
+ mci->fake_inject_layer[0],
+ mci->fake_inject_layer[1],
+ mci->fake_inject_layer[2]
+ );
+ edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
+ mci->fake_inject_layer[0],
+ mci->fake_inject_layer[1],
+ mci->fake_inject_layer[2],
+ "FAKE ERROR", "for EDAC testing only");
- return -EIO;
+ return count;
}
-/* Intermediate show/store table */
-static const struct sysfs_ops mci_ops = {
- .show = mcidev_show,
- .store = mcidev_store
-};
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
-#define MCIDEV_ATTR(_name,_mode,_show,_store) \
-static struct mcidev_sysfs_attribute mci_attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .show = _show, \
- .store = _store, \
+static const struct file_operations debug_fake_inject_fops = {
+ .open = debugfs_open,
+ .write = edac_fake_inject_write,
+ .llseek = generic_file_llseek,
};
+#endif
/* default Control file */
-MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
+DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
/* default Attribute files */
-MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
-MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
-MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
-MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
-MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
-MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
-MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
+DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
+DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
+DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
+DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
+DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
+DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
+DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
+DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
/* memory scrubber attribute file */
-MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
+DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
mci_sdram_scrub_rate_store);
-static struct mcidev_sysfs_attribute *mci_attr[] = {
- &mci_attr_reset_counters,
- &mci_attr_mc_name,
- &mci_attr_size_mb,
- &mci_attr_seconds_since_reset,
- &mci_attr_ue_noinfo_count,
- &mci_attr_ce_noinfo_count,
- &mci_attr_ue_count,
- &mci_attr_ce_count,
- &mci_attr_sdram_scrub_rate,
+static struct attribute *mci_attrs[] = {
+ &dev_attr_reset_counters.attr,
+ &dev_attr_mc_name.attr,
+ &dev_attr_size_mb.attr,
+ &dev_attr_seconds_since_reset.attr,
+ &dev_attr_ue_noinfo_count.attr,
+ &dev_attr_ce_noinfo_count.attr,
+ &dev_attr_ue_count.attr,
+ &dev_attr_ce_count.attr,
+ &dev_attr_sdram_scrub_rate.attr,
+ &dev_attr_max_location.attr,
NULL
};
+static struct attribute_group mci_attr_grp = {
+ .attrs = mci_attrs,
+};
-/*
- * Release of a MC controlling instance
- *
- * each MC control instance has the following resources upon entry:
- * a) a ref count on the top memctl kobj
- * b) a ref count on this module
- *
- * this function must decrement those ref counts and then
- * issue a free on the instance's memory
- */
-static void edac_mci_control_release(struct kobject *kobj)
-{
- struct mem_ctl_info *mci;
-
- mci = to_mci(kobj);
+static const struct attribute_group *mci_attr_groups[] = {
+ &mci_attr_grp,
+ NULL
+};
- debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx);
+static void mci_attr_release(struct device *dev)
+{
+ struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
- /* decrement the module ref count */
- module_put(mci->owner);
+ edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
+ kfree(mci);
}
-static struct kobj_type ktype_mci = {
- .release = edac_mci_control_release,
- .sysfs_ops = &mci_ops,
- .default_attrs = (struct attribute **)mci_attr,
+static struct device_type mci_attr_type = {
+ .groups = mci_attr_groups,
+ .release = mci_attr_release,
};
-/* EDAC memory controller sysfs kset:
- * /sys/devices/system/edac/mc
- */
-static struct kset *mc_kset;
+#ifdef CONFIG_EDAC_DEBUG
+static struct dentry *edac_debugfs;
-/*
- * edac_mc_register_sysfs_main_kobj
- *
- * setups and registers the main kobject for each mci
- */
-int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
+int __init edac_debugfs_init(void)
{
- struct kobject *kobj_mci;
- int err;
-
- debugf1("%s()\n", __func__);
-
- kobj_mci = &mci->edac_mci_kobj;
-
- /* Init the mci's kobject */
- memset(kobj_mci, 0, sizeof(*kobj_mci));
-
- /* Record which module 'owns' this control structure
- * and bump the ref count of the module
- */
- mci->owner = THIS_MODULE;
-
- /* bump ref count on this module */
- if (!try_module_get(mci->owner)) {
- err = -ENODEV;
- goto fail_out;
- }
-
- /* this instance become part of the mc_kset */
- kobj_mci->kset = mc_kset;
-
- /* register the mc<id> kobject to the mc_kset */
- err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL,
- "mc%d", mci->mc_idx);
- if (err) {
- debugf1("%s()Failed to register '.../edac/mc%d'\n",
- __func__, mci->mc_idx);
- goto kobj_reg_fail;
+ edac_debugfs = debugfs_create_dir("edac", NULL);
+ if (IS_ERR(edac_debugfs)) {
+ edac_debugfs = NULL;
+ return -ENOMEM;
}
- kobject_uevent(kobj_mci, KOBJ_ADD);
-
- /* At this point, to 'free' the control struct,
- * edac_mc_unregister_sysfs_main_kobj() must be used
- */
-
- debugf1("%s() Registered '.../edac/mc%d' kobject\n",
- __func__, mci->mc_idx);
-
return 0;
-
- /* Error exit stack */
-
-kobj_reg_fail:
- module_put(mci->owner);
-
-fail_out:
- return err;
-}
-
-/*
- * edac_mc_register_sysfs_main_kobj
- *
- * tears down and the main mci kobject from the mc_kset
- */
-void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
-{
- debugf1("%s()\n", __func__);
-
- /* delete the kobj from the mc_kset */
- kobject_put(&mci->edac_mci_kobj);
-}
-
-#define EDAC_DEVICE_SYMLINK "device"
-
-#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci)
-
-/* MCI show/store functions for top most object */
-static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr,
- char *buffer)
-{
- struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
- struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
- debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
- if (mcidev_attr->show)
- return mcidev_attr->show(mem_ctl_info, buffer);
-
- return -EIO;
}
-static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr,
- const char *buffer, size_t count)
+void __exit edac_debugfs_exit(void)
{
- struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
- struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
-
- debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info);
-
- if (mcidev_attr->store)
- return mcidev_attr->store(mem_ctl_info, buffer, count);
-
- return -EIO;
+ debugfs_remove(edac_debugfs);
}
-/* No memory to release for this kobj */
-static void edac_inst_grp_release(struct kobject *kobj)
+int edac_create_debug_nodes(struct mem_ctl_info *mci)
{
- struct mcidev_sysfs_group_kobj *grp;
- struct mem_ctl_info *mci;
-
- debugf1("%s()\n", __func__);
-
- grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj);
- mci = grp->mci;
-}
-
-/* Intermediate show/store table */
-static struct sysfs_ops inst_grp_ops = {
- .show = inst_grp_show,
- .store = inst_grp_store
-};
-
-/* the kobj_type instance for a instance group */
-static struct kobj_type ktype_inst_grp = {
- .release = edac_inst_grp_release,
- .sysfs_ops = &inst_grp_ops,
-};
-
+ struct dentry *d, *parent;
+ char name[80];
+ int i;
-/*
- * edac_create_mci_instance_attributes
- * create MC driver specific attributes bellow an specified kobj
- * This routine calls itself recursively, in order to create an entire
- * object tree.
- */
-static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
- const struct mcidev_sysfs_attribute *sysfs_attrib,
- struct kobject *kobj)
-{
- int err;
+ if (!edac_debugfs)
+ return -ENODEV;
- debugf4("%s()\n", __func__);
-
- while (sysfs_attrib) {
- debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
- if (sysfs_attrib->grp) {
- struct mcidev_sysfs_group_kobj *grp_kobj;
-
- grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL);
- if (!grp_kobj)
- return -ENOMEM;
-
- grp_kobj->grp = sysfs_attrib->grp;
- grp_kobj->mci = mci;
- list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
-
- debugf0("%s() grp %s, mci %p\n", __func__,
- sysfs_attrib->grp->name, mci);
-
- err = kobject_init_and_add(&grp_kobj->kobj,
- &ktype_inst_grp,
- &mci->edac_mci_kobj,
- sysfs_attrib->grp->name);
- if (err < 0) {
- printk(KERN_ERR "kobject_init_and_add failed: %d\n", err);
- return err;
- }
- err = edac_create_mci_instance_attributes(mci,
- grp_kobj->grp->mcidev_attr,
- &grp_kobj->kobj);
-
- if (err < 0)
- return err;
- } else if (sysfs_attrib->attr.name) {
- debugf4("%s() file %s\n", __func__,
- sysfs_attrib->attr.name);
-
- err = sysfs_create_file(kobj, &sysfs_attrib->attr);
- if (err < 0) {
- printk(KERN_ERR "sysfs_create_file failed: %d\n", err);
- return err;
- }
- } else
- break;
-
- sysfs_attrib++;
+ d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
+ if (!d)
+ return -ENOMEM;
+ parent = d;
+
+ for (i = 0; i < mci->n_layers; i++) {
+ sprintf(name, "fake_inject_%s",
+ edac_layer_name[mci->layers[i].type]);
+ d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_layer[i]);
+ if (!d)
+ goto nomem;
}
- return 0;
-}
+ d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_ue);
+ if (!d)
+ goto nomem;
-/*
- * edac_remove_mci_instance_attributes
- * remove MC driver specific attributes at the topmost level
- * directory of this mci instance.
- */
-static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
- const struct mcidev_sysfs_attribute *sysfs_attrib,
- struct kobject *kobj, int count)
-{
- struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
+ d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
+ &mci->fake_inject_count);
+ if (!d)
+ goto nomem;
- debugf1("%s()\n", __func__);
-
- /*
- * loop if there are attributes and until we hit a NULL entry
- * Remove first all the attributes
- */
- while (sysfs_attrib) {
- debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib);
- if (sysfs_attrib->grp) {
- debugf4("%s() seeking for group %s\n",
- __func__, sysfs_attrib->grp->name);
- list_for_each_entry(grp_kobj,
- &mci->grp_kobj_list, list) {
- debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp);
- if (grp_kobj->grp == sysfs_attrib->grp) {
- edac_remove_mci_instance_attributes(mci,
- grp_kobj->grp->mcidev_attr,
- &grp_kobj->kobj, count + 1);
- debugf4("%s() group %s\n", __func__,
- sysfs_attrib->grp->name);
- kobject_put(&grp_kobj->kobj);
- }
- }
- debugf4("%s() end of seeking for group %s\n",
- __func__, sysfs_attrib->grp->name);
- } else if (sysfs_attrib->attr.name) {
- debugf4("%s() file %s\n", __func__,
- sysfs_attrib->attr.name);
- sysfs_remove_file(kobj, &sysfs_attrib->attr);
- } else
- break;
- sysfs_attrib++;
- }
+ d = debugfs_create_file("fake_inject", S_IWUSR, parent,
+ &mci->dev,
+ &debug_fake_inject_fops);
+ if (!d)
+ goto nomem;
- /* Remove the group objects */
- if (count)
- return;
- list_for_each_entry_safe(grp_kobj, tmp,
- &mci->grp_kobj_list, list) {
- list_del(&grp_kobj->list);
- kfree(grp_kobj);
- }
+ mci->debugfs = parent;
+ return 0;
+nomem:
+ debugfs_remove(mci->debugfs);
+ return -ENOMEM;
}
-
+#endif
/*
* Create a new Memory Controller kobject instance,
@@ -906,77 +979,87 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
*/
int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
{
- int i, j;
- int err;
- struct csrow_info *csrow;
- struct kobject *kobj_mci = &mci->edac_mci_kobj;
+ int i, err;
- debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
-
- INIT_LIST_HEAD(&mci->grp_kobj_list);
+ /*
+ * The memory controller needs its own bus, in order to avoid
+ * namespace conflicts at /sys/bus/edac.
+ */
+ mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
+ if (!mci->bus.name)
+ return -ENOMEM;
+ edac_dbg(0, "creating bus %s\n", mci->bus.name);
+ err = bus_register(&mci->bus);
+ if (err < 0)
+ return err;
- /* create a symlink for the device */
- err = sysfs_create_link(kobj_mci, &mci->dev->kobj,
- EDAC_DEVICE_SYMLINK);
- if (err) {
- debugf1("%s() failure to create symlink\n", __func__);
- goto fail0;
+ /* get the /sys/devices/system/edac subsys reference */
+ mci->dev.type = &mci_attr_type;
+ device_initialize(&mci->dev);
+
+ mci->dev.parent = mci_pdev;
+ mci->dev.bus = &mci->bus;
+ dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
+ dev_set_drvdata(&mci->dev, mci);
+ pm_runtime_forbid(&mci->dev);
+
+ edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
+ err = device_add(&mci->dev);
+ if (err < 0) {
+ bus_unregister(&mci->bus);
+ kfree(mci->bus.name);
+ return err;
}
- /* If the low level driver desires some attributes,
- * then create them now for the driver.
+ /*
+ * Create the dimm/rank devices
*/
- if (mci->mc_driver_sysfs_attributes) {
- err = edac_create_mci_instance_attributes(mci,
- mci->mc_driver_sysfs_attributes,
- &mci->edac_mci_kobj);
+ for (i = 0; i < mci->tot_dimms; i++) {
+ struct dimm_info *dimm = mci->dimms[i];
+ /* Only expose populated DIMMs */
+ if (dimm->nr_pages == 0)
+ continue;
+#ifdef CONFIG_EDAC_DEBUG
+ edac_dbg(1, "creating dimm%d, located at ", i);
+ if (edac_debug_level >= 1) {
+ int lay;
+ for (lay = 0; lay < mci->n_layers; lay++)
+ printk(KERN_CONT "%s %d ",
+ edac_layer_name[mci->layers[lay].type],
+ dimm->location[lay]);
+ printk(KERN_CONT "\n");
+ }
+#endif
+ err = edac_create_dimm_object(mci, dimm, i);
if (err) {
- debugf1("%s() failure to create mci attributes\n",
- __func__);
- goto fail0;
+ edac_dbg(1, "failure: create dimm %d obj\n", i);
+ goto fail;
}
}
- /* Make directories for each CSROW object under the mc<id> kobject
- */
- for (i = 0; i < mci->nr_csrows; i++) {
- int nr_pages = 0;
-
- csrow = &mci->csrows[i];
- for (j = 0; j < csrow->nr_channels; j++)
- nr_pages += csrow->channels[j].dimm->nr_pages;
-
- if (nr_pages > 0) {
- err = edac_create_csrow_object(mci, csrow, i);
- if (err) {
- debugf1("%s() failure: create csrow %d obj\n",
- __func__, i);
- goto fail1;
- }
- }
- }
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+ err = edac_create_csrow_objects(mci);
+ if (err < 0)
+ goto fail;
+#endif
+#ifdef CONFIG_EDAC_DEBUG
+ edac_create_debug_nodes(mci);
+#endif
return 0;
-fail1:
+fail:
for (i--; i >= 0; i--) {
- int nr_pages = 0;
-
- csrow = &mci->csrows[i];
- for (j = 0; j < csrow->nr_channels; j++)
- nr_pages += csrow->channels[j].dimm->nr_pages;
- if (nr_pages > 0)
- kobject_put(&mci->csrows[i].kobj);
+ struct dimm_info *dimm = mci->dimms[i];
+ if (dimm->nr_pages == 0)
+ continue;
+ put_device(&dimm->dev);
+ device_del(&dimm->dev);
}
-
- /* remove the mci instance's attributes, if any */
- edac_remove_mci_instance_attributes(mci,
- mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0);
-
- /* remove the symlink */
- sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
-
-fail0:
+ put_device(&mci->dev);
+ device_del(&mci->dev);
+ bus_unregister(&mci->bus);
+ kfree(mci->bus.name);
return err;
}
@@ -985,98 +1068,84 @@ fail0:
*/
void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
{
- struct csrow_info *csrow;
- int i, j;
-
- debugf0("%s()\n", __func__);
-
- /* remove all csrow kobjects */
- debugf4("%s() unregister this mci kobj\n", __func__);
- for (i = 0; i < mci->nr_csrows; i++) {
- int nr_pages = 0;
-
- csrow = &mci->csrows[i];
- for (j = 0; j < csrow->nr_channels; j++)
- nr_pages += csrow->channels[j].dimm->nr_pages;
- if (nr_pages > 0) {
- debugf0("%s() unreg csrow-%d\n", __func__, i);
- kobject_put(&mci->csrows[i].kobj);
- }
- }
+ int i;
- /* remove this mci instance's attribtes */
- if (mci->mc_driver_sysfs_attributes) {
- debugf4("%s() unregister mci private attributes\n", __func__);
- edac_remove_mci_instance_attributes(mci,
- mci->mc_driver_sysfs_attributes,
- &mci->edac_mci_kobj, 0);
+ edac_dbg(0, "\n");
+
+#ifdef CONFIG_EDAC_DEBUG
+ debugfs_remove(mci->debugfs);
+#endif
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
+ edac_delete_csrow_objects(mci);
+#endif
+
+ for (i = 0; i < mci->tot_dimms; i++) {
+ struct dimm_info *dimm = mci->dimms[i];
+ if (dimm->nr_pages == 0)
+ continue;
+ edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
+ put_device(&dimm->dev);
+ device_del(&dimm->dev);
}
-
- /* remove the symlink */
- debugf4("%s() remove_link\n", __func__);
- sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
-
- /* unregister this instance's kobject */
- debugf4("%s() remove_mci_instance\n", __func__);
- kobject_put(&mci->edac_mci_kobj);
}
+void edac_unregister_sysfs(struct mem_ctl_info *mci)
+{
+ edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
+ put_device(&mci->dev);
+ device_del(&mci->dev);
+ bus_unregister(&mci->bus);
+ kfree(mci->bus.name);
+}
+static void mc_attr_release(struct device *dev)
+{
+ /*
+ * There's no container structure here, as this is just the mci
+ * parent device, used to create the /sys/devices/mc sysfs node.
+ * So, there are no attributes on it.
+ */
+ edac_dbg(1, "Releasing device %s\n", dev_name(dev));
+ kfree(dev);
+}
-
+static struct device_type mc_attr_type = {
+ .release = mc_attr_release,
+};
/*
- * edac_setup_sysfs_mc_kset(void)
- *
- * Initialize the mc_kset for the 'mc' entry
- * This requires creating the top 'mc' directory with a kset
- * and its controls/attributes.
- *
- * To this 'mc' kset, instance 'mci' will be grouped as children.
- *
- * Return: 0 SUCCESS
- * !0 FAILURE error code
+ * Init/exit code for the module. Basically, creates/removes /sys/class/rc
*/
-int edac_sysfs_setup_mc_kset(void)
+int __init edac_mc_sysfs_init(void)
{
- int err = -EINVAL;
struct bus_type *edac_subsys;
-
- debugf1("%s()\n", __func__);
+ int err;
/* get the /sys/devices/system/edac subsys reference */
edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
- debugf1("%s() no edac_subsys error=%d\n", __func__, err);
- goto fail_out;
+ edac_dbg(1, "no edac_subsys\n");
+ return -EINVAL;
}
- /* Init the MC's kobject */
- mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj);
- if (!mc_kset) {
- err = -ENOMEM;
- debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
- goto fail_kset;
- }
+ mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
- debugf1("%s() Registered '.../edac/mc' kobject\n", __func__);
+ mci_pdev->bus = edac_subsys;
+ mci_pdev->type = &mc_attr_type;
+ device_initialize(mci_pdev);
+ dev_set_name(mci_pdev, "mc");
- return 0;
+ err = device_add(mci_pdev);
+ if (err < 0)
+ return err;
-fail_kset:
- edac_put_sysfs_subsys();
+ edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
-fail_out:
- return err;
+ return 0;
}
-/*
- * edac_sysfs_teardown_mc_kset
- *
- * deconstruct the mc_ket for memory controllers
- */
-void edac_sysfs_teardown_mc_kset(void)
+void __exit edac_mc_sysfs_exit(void)
{
- kset_unregister(mc_kset);
+ put_device(mci_pdev);
+ device_del(mci_pdev);
edac_put_sysfs_subsys();
}
-
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index 5ddaa86d6a6e..58a28d838f37 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -15,7 +15,7 @@
#include "edac_core.h"
#include "edac_module.h"
-#define EDAC_VERSION "Ver: 2.1.0"
+#define EDAC_VERSION "Ver: 3.0.0"
#ifdef CONFIG_EDAC_DEBUG
/* Values of 0 to 4 will generate output */
@@ -90,26 +90,21 @@ static int __init edac_init(void)
*/
edac_pci_clear_parity_errors();
- /*
- * now set up the mc_kset under the edac class object
- */
- err = edac_sysfs_setup_mc_kset();
+ err = edac_mc_sysfs_init();
if (err)
goto error;
+ edac_debugfs_init();
+
/* Setup/Initialize the workq for this core */
err = edac_workqueue_setup();
if (err) {
edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n");
- goto workq_fail;
+ goto error;
}
return 0;
- /* Error teardown stack */
-workq_fail:
- edac_sysfs_teardown_mc_kset();
-
error:
return err;
}
@@ -120,11 +115,12 @@ error:
*/
static void __exit edac_exit(void)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* tear down the various subsystems */
edac_workqueue_teardown();
- edac_sysfs_teardown_mc_kset();
+ edac_mc_sysfs_exit();
+ edac_debugfs_exit();
}
/*
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h
index 0ea7d14cb930..3d139c6e7fe3 100644
--- a/drivers/edac/edac_module.h
+++ b/drivers/edac/edac_module.h
@@ -19,12 +19,12 @@
*
* edac_mc objects
*/
-extern int edac_sysfs_setup_mc_kset(void);
-extern void edac_sysfs_teardown_mc_kset(void);
-extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci);
-extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci);
+ /* on edac_mc_sysfs.c */
+int edac_mc_sysfs_init(void);
+void edac_mc_sysfs_exit(void);
extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci);
extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci);
+void edac_unregister_sysfs(struct mem_ctl_info *mci);
extern int edac_get_log_ue(void);
extern int edac_get_log_ce(void);
extern int edac_get_panic_on_ue(void);
@@ -34,6 +34,10 @@ extern int edac_mc_get_panic_on_ue(void);
extern int edac_get_poll_msec(void);
extern int edac_mc_get_poll_msec(void);
+unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
+ unsigned len);
+
+ /* on edac_device.c */
extern int edac_device_register_sysfs_main_kobj(
struct edac_device_ctl_info *edac_dev);
extern void edac_device_unregister_sysfs_main_kobj(
@@ -53,6 +57,20 @@ extern void edac_mc_reset_delay_period(int value);
extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
/*
+ * EDAC debugfs functions
+ */
+#ifdef CONFIG_EDAC_DEBUG
+int edac_debugfs_init(void);
+void edac_debugfs_exit(void);
+#else
+static inline int edac_debugfs_init(void)
+{
+ return -ENODEV;
+}
+static inline void edac_debugfs_exit(void) {}
+#endif
+
+/*
* EDAC PCI functions
*/
#ifdef CONFIG_PCI
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index f1ac86649886..ee87ef972ead 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -45,7 +45,7 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
void *p = NULL, *pvt;
unsigned int size;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
pci = edac_align_ptr(&p, sizeof(*pci), 1);
pvt = edac_align_ptr(&p, 1, sz_pvt);
@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);
*/
void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)
{
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
edac_pci_remove_sysfs(pci);
}
@@ -97,7 +97,7 @@ static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)
struct edac_pci_ctl_info *pci;
struct list_head *item;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
list_for_each(item, &edac_pci_list) {
pci = list_entry(item, struct edac_pci_ctl_info, link);
@@ -122,7 +122,7 @@ static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)
struct list_head *item, *insert_before;
struct edac_pci_ctl_info *rover;
- debugf1("%s()\n", __func__);
+ edac_dbg(1, "\n");
insert_before = &edac_pci_list;
@@ -226,7 +226,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
int msec;
unsigned long delay;
- debugf3("%s() checking\n", __func__);
+ edac_dbg(3, "checking\n");
mutex_lock(&edac_pci_ctls_mutex);
@@ -261,7 +261,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
unsigned int msec)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
queue_delayed_work(edac_workqueue, &pci->work,
@@ -276,7 +276,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
{
int status;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
status = cancel_delayed_work(&pci->work);
if (status == 0)
@@ -293,7 +293,7 @@ static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
unsigned long value)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
edac_pci_workq_teardown(pci);
@@ -333,7 +333,7 @@ EXPORT_SYMBOL_GPL(edac_pci_alloc_index);
*/
int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
pci->pci_idx = edac_idx;
pci->start_time = jiffies;
@@ -393,7 +393,7 @@ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)
{
struct edac_pci_ctl_info *pci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mutex_lock(&edac_pci_ctls_mutex);
@@ -430,7 +430,7 @@ EXPORT_SYMBOL_GPL(edac_pci_del_device);
*/
static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)
{
- debugf4("%s()\n", __func__);
+ edac_dbg(4, "\n");
edac_pci_do_parity_check();
}
@@ -475,7 +475,7 @@ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,
pdata->edac_idx = edac_pci_idx++;
if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
- debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_pci_add_device()\n");
edac_pci_free_ctl_info(pci);
return NULL;
}
@@ -491,7 +491,7 @@ EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);
*/
void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)
{
- debugf0("%s() pci mod=%s\n", __func__, pci->mod_name);
+ edac_dbg(0, "pci mod=%s\n", pci->mod_name);
edac_pci_del_device(pci->dev);
edac_pci_free_ctl_info(pci);
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c
index 97f5064e3992..e164c555a337 100644
--- a/drivers/edac/edac_pci_sysfs.c
+++ b/drivers/edac/edac_pci_sysfs.c
@@ -78,7 +78,7 @@ static void edac_pci_instance_release(struct kobject *kobj)
{
struct edac_pci_ctl_info *pci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* Form pointer to containing struct, the pci control struct */
pci = to_instance(kobj);
@@ -161,7 +161,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
struct kobject *main_kobj;
int err;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* First bump the ref count on the top main kobj, which will
* track the number of PCI instances we have, and thus nest
@@ -177,14 +177,13 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance,
edac_pci_top_main_kobj, "pci%d", idx);
if (err != 0) {
- debugf2("%s() failed to register instance pci%d\n",
- __func__, idx);
+ edac_dbg(2, "failed to register instance pci%d\n", idx);
kobject_put(edac_pci_top_main_kobj);
goto error_out;
}
kobject_uevent(&pci->kobj, KOBJ_ADD);
- debugf1("%s() Register instance 'pci%d' kobject\n", __func__, idx);
+ edac_dbg(1, "Register instance 'pci%d' kobject\n", idx);
return 0;
@@ -201,7 +200,7 @@ error_out:
static void edac_pci_unregister_sysfs_instance_kobj(
struct edac_pci_ctl_info *pci)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* Unregister the instance kobject and allow its release
* function release the main reference count and then
@@ -317,7 +316,7 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = {
*/
static void edac_pci_release_main_kobj(struct kobject *kobj)
{
- debugf0("%s() here to module_put(THIS_MODULE)\n", __func__);
+ edac_dbg(0, "here to module_put(THIS_MODULE)\n");
kfree(kobj);
@@ -345,7 +344,7 @@ static int edac_pci_main_kobj_setup(void)
int err;
struct bus_type *edac_subsys;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* check and count if we have already created the main kobject */
if (atomic_inc_return(&edac_pci_sysfs_refcount) != 1)
@@ -356,7 +355,7 @@ static int edac_pci_main_kobj_setup(void)
*/
edac_subsys = edac_get_sysfs_subsys();
if (edac_subsys == NULL) {
- debugf1("%s() no edac_subsys\n", __func__);
+ edac_dbg(1, "no edac_subsys\n");
err = -ENODEV;
goto decrement_count_fail;
}
@@ -366,14 +365,14 @@ static int edac_pci_main_kobj_setup(void)
* level main kobj for EDAC PCI
*/
if (!try_module_get(THIS_MODULE)) {
- debugf1("%s() try_module_get() failed\n", __func__);
+ edac_dbg(1, "try_module_get() failed\n");
err = -ENODEV;
goto mod_get_fail;
}
edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
if (!edac_pci_top_main_kobj) {
- debugf1("Failed to allocate\n");
+ edac_dbg(1, "Failed to allocate\n");
err = -ENOMEM;
goto kzalloc_fail;
}
@@ -383,7 +382,7 @@ static int edac_pci_main_kobj_setup(void)
&ktype_edac_pci_main_kobj,
&edac_subsys->dev_root->kobj, "pci");
if (err) {
- debugf1("Failed to register '.../edac/pci'\n");
+ edac_dbg(1, "Failed to register '.../edac/pci'\n");
goto kobject_init_and_add_fail;
}
@@ -392,7 +391,7 @@ static int edac_pci_main_kobj_setup(void)
* must be used, for resources to be cleaned up properly
*/
kobject_uevent(edac_pci_top_main_kobj, KOBJ_ADD);
- debugf1("Registered '.../edac/pci' kobject\n");
+ edac_dbg(1, "Registered '.../edac/pci' kobject\n");
return 0;
@@ -421,15 +420,14 @@ decrement_count_fail:
*/
static void edac_pci_main_kobj_teardown(void)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* Decrement the count and only if no more controller instances
* are connected perform the unregisteration of the top level
* main kobj
*/
if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
- debugf0("%s() called kobject_put on main kobj\n",
- __func__);
+ edac_dbg(0, "called kobject_put on main kobj\n");
kobject_put(edac_pci_top_main_kobj);
}
edac_put_sysfs_subsys();
@@ -446,7 +444,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci)
int err;
struct kobject *edac_kobj = &pci->kobj;
- debugf0("%s() idx=%d\n", __func__, pci->pci_idx);
+ edac_dbg(0, "idx=%d\n", pci->pci_idx);
/* create the top main EDAC PCI kobject, IF needed */
err = edac_pci_main_kobj_setup();
@@ -460,8 +458,7 @@ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci)
err = sysfs_create_link(edac_kobj, &pci->dev->kobj, EDAC_PCI_SYMLINK);
if (err) {
- debugf0("%s() sysfs_create_link() returned err= %d\n",
- __func__, err);
+ edac_dbg(0, "sysfs_create_link() returned err= %d\n", err);
goto symlink_fail;
}
@@ -484,7 +481,7 @@ unregister_cleanup:
*/
void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci)
{
- debugf0("%s() index=%d\n", __func__, pci->pci_idx);
+ edac_dbg(0, "index=%d\n", pci->pci_idx);
/* Remove the symlink */
sysfs_remove_link(&pci->kobj, EDAC_PCI_SYMLINK);
@@ -496,7 +493,7 @@ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci)
* if this 'pci' is the last instance.
* If it is, the main kobject will be unregistered as a result
*/
- debugf0("%s() calling edac_pci_main_kobj_teardown()\n", __func__);
+ edac_dbg(0, "calling edac_pci_main_kobj_teardown()\n");
edac_pci_main_kobj_teardown();
}
@@ -572,7 +569,7 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
local_irq_restore(flags);
- debugf4("PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
+ edac_dbg(4, "PCI STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
/* check the status reg for errors on boards NOT marked as broken
* if broken, we cannot trust any of the status bits
@@ -603,13 +600,15 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
}
- debugf4("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev_name(&dev->dev));
+ edac_dbg(4, "PCI HEADER TYPE= 0x%02x %s\n",
+ header_type, dev_name(&dev->dev));
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
/* On bridges, need to examine secondary status register */
status = get_pci_parity_status(dev, 1);
- debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev_name(&dev->dev));
+ edac_dbg(4, "PCI SEC_STATUS= 0x%04x %s\n",
+ status, dev_name(&dev->dev));
/* check the secondary status reg for errors,
* on NOT broken boards
@@ -671,7 +670,7 @@ void edac_pci_do_parity_check(void)
{
int before_count;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* if policy has PCI check off, leave now */
if (!check_pci_errors)
diff --git a/drivers/edac/highbank_l2_edac.c b/drivers/edac/highbank_l2_edac.c
new file mode 100644
index 000000000000..e599b00c05a8
--- /dev/null
+++ b/drivers/edac/highbank_l2_edac.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define SR_CLR_SB_ECC_INTR 0x0
+#define SR_CLR_DB_ECC_INTR 0x4
+
+struct hb_l2_drvdata {
+ void __iomem *base;
+ int sb_irq;
+ int db_irq;
+};
+
+static irqreturn_t highbank_l2_err_handler(int irq, void *dev_id)
+{
+ struct edac_device_ctl_info *dci = dev_id;
+ struct hb_l2_drvdata *drvdata = dci->pvt_info;
+
+ if (irq == drvdata->sb_irq) {
+ writel(1, drvdata->base + SR_CLR_SB_ECC_INTR);
+ edac_device_handle_ce(dci, 0, 0, dci->ctl_name);
+ }
+ if (irq == drvdata->db_irq) {
+ writel(1, drvdata->base + SR_CLR_DB_ECC_INTR);
+ edac_device_handle_ue(dci, 0, 0, dci->ctl_name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit highbank_l2_err_probe(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *dci;
+ struct hb_l2_drvdata *drvdata;
+ struct resource *r;
+ int res = 0;
+
+ dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "cpu",
+ 1, "L", 1, 2, NULL, 0, 0);
+ if (!dci)
+ return -ENOMEM;
+
+ drvdata = dci->pvt_info;
+ dci->dev = &pdev->dev;
+ platform_set_drvdata(pdev, dci);
+
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "Unable to get mem resource\n");
+ res = -ENODEV;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, r->start,
+ resource_size(r), dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "Error while requesting mem region\n");
+ res = -EBUSY;
+ goto err;
+ }
+
+ drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!drvdata->base) {
+ dev_err(&pdev->dev, "Unable to map regs\n");
+ res = -ENOMEM;
+ goto err;
+ }
+
+ drvdata->db_irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev, drvdata->db_irq,
+ highbank_l2_err_handler,
+ 0, dev_name(&pdev->dev), dci);
+ if (res < 0)
+ goto err;
+
+ drvdata->sb_irq = platform_get_irq(pdev, 1);
+ res = devm_request_irq(&pdev->dev, drvdata->sb_irq,
+ highbank_l2_err_handler,
+ 0, dev_name(&pdev->dev), dci);
+ if (res < 0)
+ goto err;
+
+ dci->mod_name = dev_name(&pdev->dev);
+ dci->dev_name = dev_name(&pdev->dev);
+
+ if (edac_device_add_device(dci))
+ goto err;
+
+ devres_close_group(&pdev->dev, NULL);
+ return 0;
+err:
+ devres_release_group(&pdev->dev, NULL);
+ edac_device_free_ctl_info(dci);
+ return res;
+}
+
+static int highbank_l2_err_remove(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
+
+ edac_device_del_device(&pdev->dev);
+ edac_device_free_ctl_info(dci);
+ return 0;
+}
+
+static const struct of_device_id hb_l2_err_of_match[] = {
+ { .compatible = "calxeda,hb-sregs-l2-ecc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hb_l2_err_of_match);
+
+static struct platform_driver highbank_l2_edac_driver = {
+ .probe = highbank_l2_err_probe,
+ .remove = highbank_l2_err_remove,
+ .driver = {
+ .name = "hb_l2_edac",
+ .of_match_table = hb_l2_err_of_match,
+ },
+};
+
+module_platform_driver(highbank_l2_edac_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Calxeda, Inc.");
+MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank L2 Cache");
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
new file mode 100644
index 000000000000..c769f477fd22
--- /dev/null
+++ b/drivers/edac/highbank_mc_edac.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/uaccess.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+/* DDR Ctrlr Error Registers */
+#define HB_DDR_ECC_OPT 0x128
+#define HB_DDR_ECC_U_ERR_ADDR 0x130
+#define HB_DDR_ECC_U_ERR_STAT 0x134
+#define HB_DDR_ECC_U_ERR_DATAL 0x138
+#define HB_DDR_ECC_U_ERR_DATAH 0x13c
+#define HB_DDR_ECC_C_ERR_ADDR 0x140
+#define HB_DDR_ECC_C_ERR_STAT 0x144
+#define HB_DDR_ECC_C_ERR_DATAL 0x148
+#define HB_DDR_ECC_C_ERR_DATAH 0x14c
+#define HB_DDR_ECC_INT_STATUS 0x180
+#define HB_DDR_ECC_INT_ACK 0x184
+#define HB_DDR_ECC_U_ERR_ID 0x424
+#define HB_DDR_ECC_C_ERR_ID 0x428
+
+#define HB_DDR_ECC_INT_STAT_CE 0x8
+#define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10
+#define HB_DDR_ECC_INT_STAT_UE 0x20
+#define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40
+
+#define HB_DDR_ECC_OPT_MODE_MASK 0x3
+#define HB_DDR_ECC_OPT_FWC 0x100
+#define HB_DDR_ECC_OPT_XOR_SHIFT 16
+
+struct hb_mc_drvdata {
+ void __iomem *mc_vbase;
+};
+
+static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct hb_mc_drvdata *drvdata = mci->pvt_info;
+ u32 status, err_addr;
+
+ /* Read the interrupt status register */
+ status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS);
+
+ if (status & HB_DDR_ECC_INT_STAT_UE) {
+ err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_U_ERR_ADDR);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+ err_addr >> PAGE_SHIFT,
+ err_addr & ~PAGE_MASK, 0,
+ 0, 0, -1,
+ mci->ctl_name, "");
+ }
+ if (status & HB_DDR_ECC_INT_STAT_CE) {
+ u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT);
+ syndrome = (syndrome >> 8) & 0xff;
+ err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+ err_addr >> PAGE_SHIFT,
+ err_addr & ~PAGE_MASK, syndrome,
+ 0, 0, -1,
+ mci->ctl_name, "");
+ }
+
+ /* clear the error, clears the interrupt */
+ writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t highbank_mc_err_inject_write(struct file *file,
+ const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct mem_ctl_info *mci = file->private_data;
+ struct hb_mc_drvdata *pdata = mci->pvt_info;
+ char buf[32];
+ size_t buf_size;
+ u32 reg;
+ u8 synd;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, data, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ if (!kstrtou8(buf, 16, &synd)) {
+ reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT);
+ reg &= HB_DDR_ECC_OPT_MODE_MASK;
+ reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC;
+ writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT);
+ }
+
+ return count;
+}
+
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations highbank_mc_debug_inject_fops = {
+ .open = debugfs_open,
+ .write = highbank_mc_err_inject_write,
+ .llseek = generic_file_llseek,
+};
+
+static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{
+ if (mci->debugfs)
+ debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+ &highbank_mc_debug_inject_fops);
+;
+}
+#else
+static void __devinit highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{}
+#endif
+
+static int __devinit highbank_mc_probe(struct platform_device *pdev)
+{
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+ struct hb_mc_drvdata *drvdata;
+ struct dimm_info *dimm;
+ struct resource *r;
+ u32 control;
+ int irq;
+ int res = 0;
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = 1;
+ layers[1].is_virt_csrow = false;
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+ sizeof(struct hb_mc_drvdata));
+ if (!mci)
+ return -ENOMEM;
+
+ mci->pdev = &pdev->dev;
+ drvdata = mci->pvt_info;
+ platform_set_drvdata(pdev, mci);
+
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "Unable to get mem resource\n");
+ res = -ENODEV;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, r->start,
+ resource_size(r), dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "Error while requesting mem region\n");
+ res = -EBUSY;
+ goto err;
+ }
+
+ drvdata->mc_vbase = devm_ioremap(&pdev->dev,
+ r->start, resource_size(r));
+ if (!drvdata->mc_vbase) {
+ dev_err(&pdev->dev, "Unable to map regs\n");
+ res = -ENOMEM;
+ goto err;
+ }
+
+ control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3;
+ if (!control || (control == 0x2)) {
+ dev_err(&pdev->dev, "No ECC present, or ECC disabled\n");
+ res = -ENODEV;
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
+ 0, dev_name(&pdev->dev), mci);
+ if (res < 0) {
+ dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ goto err;
+ }
+
+ mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = dev_name(&pdev->dev);
+ mci->mod_ver = "1";
+ mci->ctl_name = dev_name(&pdev->dev);
+ mci->scrub_mode = SCRUB_SW_SRC;
+
+ /* Only a single 4GB DIMM is supported */
+ dimm = *mci->dimms;
+ dimm->nr_pages = (~0UL >> PAGE_SHIFT) + 1;
+ dimm->grain = 8;
+ dimm->dtype = DEV_X8;
+ dimm->mtype = MEM_DDR3;
+ dimm->edac_mode = EDAC_SECDED;
+
+ res = edac_mc_add_mc(mci);
+ if (res < 0)
+ goto err;
+
+ highbank_mc_create_debugfs_nodes(mci);
+
+ devres_close_group(&pdev->dev, NULL);
+ return 0;
+err:
+ devres_release_group(&pdev->dev, NULL);
+ edac_mc_free(mci);
+ return res;
+}
+
+static int highbank_mc_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+ return 0;
+}
+
+static const struct of_device_id hb_ddr_ctrl_of_match[] = {
+ { .compatible = "calxeda,hb-ddr-ctrl", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
+
+static struct platform_driver highbank_mc_edac_driver = {
+ .probe = highbank_mc_probe,
+ .remove = highbank_mc_remove,
+ .driver = {
+ .name = "hb_mc_edac",
+ .of_match_table = hb_ddr_ctrl_of_match,
+ },
+};
+
+module_platform_driver(highbank_mc_edac_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Calxeda, Inc.");
+MODULE_DESCRIPTION("EDAC Driver for Calxeda Highbank");
diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c
index 8ad1744faacd..d3d19cc4e9a1 100644
--- a/drivers/edac/i3000_edac.c
+++ b/drivers/edac/i3000_edac.c
@@ -194,7 +194,7 @@ static void i3000_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -236,7 +236,7 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
int row, multi_chan, channel;
unsigned long pfn, offset;
- multi_chan = mci->csrows[0].nr_channels - 1;
+ multi_chan = mci->csrows[0]->nr_channels - 1;
if (!(info->errsts & I3000_ERRSTS_BITS))
return 0;
@@ -245,9 +245,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
return 1;
if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
-1, -1, -1,
- "UE overwrote CE", "", NULL);
+ "UE overwrote CE", "");
info->errsts = info->errsts2;
}
@@ -258,15 +258,15 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
row = edac_mc_find_csrow_by_page(mci, pfn);
if (info->errsts & I3000_ERRSTS_UE)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
pfn, offset, 0,
row, -1, -1,
- "i3000 UE", "", NULL);
+ "i3000 UE", "");
else
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
pfn, offset, info->derrsyn,
row, multi_chan ? channel : 0, -1,
- "i3000 CE", "", NULL);
+ "i3000 CE", "");
return 1;
}
@@ -275,7 +275,7 @@ static void i3000_check(struct mem_ctl_info *mci)
{
struct i3000_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i3000_get_error_info(mci, &info);
i3000_process_error_info(mci, &info, 1);
}
@@ -322,7 +322,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
unsigned long mchbar;
void __iomem *window;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
pci_read_config_dword(pdev, I3000_MCHBAR, (u32 *) & mchbar);
mchbar &= I3000_MCHBAR_MASK;
@@ -366,9 +366,9 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
if (!mci)
return -ENOMEM;
- debugf3("MC: %s(): init mci\n", __func__);
+ edac_dbg(3, "MC: init mci\n");
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -393,14 +393,13 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) {
u8 value;
u32 cumul_size;
- struct csrow_info *csrow = &mci->csrows[i];
+ struct csrow_info *csrow = mci->csrows[i];
value = drb[i];
cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT);
if (interleaved)
cumul_size <<= 1;
- debugf3("MC: %s(): (%d) cumul_size 0x%x\n",
- __func__, i, cumul_size);
+ edac_dbg(3, "MC: (%d) cumul_size 0x%x\n", i, cumul_size);
if (cumul_size == last_cumul_size)
continue;
@@ -410,7 +409,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
last_cumul_size = cumul_size;
for (j = 0; j < nr_channels; j++) {
- struct dimm_info *dimm = csrow->channels[j].dimm;
+ struct dimm_info *dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / nr_channels;
dimm->grain = I3000_DEAP_GRAIN;
@@ -429,7 +428,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
rc = -ENODEV;
if (edac_mc_add_mc(mci)) {
- debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
goto fail;
}
@@ -445,7 +444,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("MC: %s(): success\n", __func__);
+ edac_dbg(3, "MC: success\n");
return 0;
fail:
@@ -461,7 +460,7 @@ static int __devinit i3000_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
@@ -477,7 +476,7 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (i3000_pci)
edac_pci_release_generic_ctl(i3000_pci);
@@ -511,7 +510,7 @@ static int __init i3000_init(void)
{
int pci_rc;
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -525,14 +524,14 @@ static int __init i3000_init(void)
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_3000_HB, NULL);
if (!mci_pdev) {
- debugf0("i3000 pci_get_device fail\n");
+ edac_dbg(0, "i3000 pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
pci_rc = i3000_init_one(mci_pdev, i3000_pci_tbl);
if (pci_rc < 0) {
- debugf0("i3000 init fail\n");
+ edac_dbg(0, "i3000 init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -552,7 +551,7 @@ fail0:
static void __exit i3000_exit(void)
{
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
pci_unregister_driver(&i3000_driver);
if (!i3000_registered) {
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index bbe43ef71823..47180a08edad 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -110,10 +110,10 @@ static int how_many_channels(struct pci_dev *pdev)
pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b);
if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
- debugf0("In single channel mode.\n");
+ edac_dbg(0, "In single channel mode\n");
return 1;
} else {
- debugf0("In dual channel mode.\n");
+ edac_dbg(0, "In dual channel mode\n");
return 2;
}
}
@@ -159,7 +159,7 @@ static void i3200_clear_error_info(struct mem_ctl_info *mci)
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* Clear any error bits.
@@ -176,7 +176,7 @@ static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci,
struct i3200_priv *priv = mci->pvt_info;
void __iomem *window = priv->window;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -218,25 +218,25 @@ static void i3200_process_error_info(struct mem_ctl_info *mci,
return;
if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
- -1, -1, -1, "UE overwrote CE", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, "UE overwrote CE", "");
info->errsts = info->errsts2;
}
for (channel = 0; channel < nr_channels; channel++) {
log = info->eccerrlog[channel];
if (log & I3200_ECCERRLOG_UE) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, 0,
eccerrlog_row(channel, log),
-1, -1,
- "i3000 UE", "", NULL);
+ "i3000 UE", "");
} else if (log & I3200_ECCERRLOG_CE) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, eccerrlog_syndrome(log),
eccerrlog_row(channel, log),
-1, -1,
- "i3000 UE", "", NULL);
+ "i3000 UE", "");
}
}
}
@@ -245,7 +245,7 @@ static void i3200_check(struct mem_ctl_info *mci)
{
struct i3200_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i3200_get_and_clear_error_info(mci, &info);
i3200_process_error_info(mci, &info);
}
@@ -332,7 +332,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
void __iomem *window;
struct i3200_priv *priv;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
window = i3200_map_mchbar(pdev);
if (!window)
@@ -352,9 +352,9 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
if (!mci)
return -ENOMEM;
- debugf3("MC: %s(): init mci\n", __func__);
+ edac_dbg(3, "MC: init mci\n");
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -379,7 +379,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
*/
for (i = 0; i < mci->nr_csrows; i++) {
unsigned long nr_pages;
- struct csrow_info *csrow = &mci->csrows[i];
+ struct csrow_info *csrow = mci->csrows[i];
nr_pages = drb_to_nr_pages(drbs, stacked,
i / I3200_RANKS_PER_CHANNEL,
@@ -389,7 +389,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
continue;
for (j = 0; j < nr_channels; j++) {
- struct dimm_info *dimm = csrow->channels[j].dimm;
+ struct dimm_info *dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / nr_channels;
dimm->grain = nr_pages << PAGE_SHIFT;
@@ -403,12 +403,12 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
rc = -ENODEV;
if (edac_mc_add_mc(mci)) {
- debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
goto fail;
}
/* get this far and it's successful */
- debugf3("MC: %s(): success\n", __func__);
+ edac_dbg(3, "MC: success\n");
return 0;
fail:
@@ -424,7 +424,7 @@ static int __devinit i3200_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
@@ -441,7 +441,7 @@ static void __devexit i3200_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
struct i3200_priv *priv;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mci = edac_mc_del_mc(&pdev->dev);
if (!mci)
@@ -475,7 +475,7 @@ static int __init i3200_init(void)
{
int pci_rc;
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -489,14 +489,14 @@ static int __init i3200_init(void)
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_3200_HB, NULL);
if (!mci_pdev) {
- debugf0("i3200 pci_get_device fail\n");
+ edac_dbg(0, "i3200 pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl);
if (pci_rc < 0) {
- debugf0("i3200 init fail\n");
+ edac_dbg(0, "i3200 init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -516,7 +516,7 @@ fail0:
static void __exit i3200_exit(void)
{
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
pci_unregister_driver(&i3200_driver);
if (!i3200_registered) {
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 11ea835f155a..39c63757c2a1 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -273,7 +273,7 @@
#define CHANNELS_PER_BRANCH 2
#define MAX_BRANCHES 2
-/* Defines to extract the vaious fields from the
+/* Defines to extract the various fields from the
* MTRx - Memory Technology Registers
*/
#define MTR_DIMMS_PRESENT(mtr) ((mtr) & (0x1 << 8))
@@ -287,22 +287,6 @@
#define MTR_DIMM_COLS(mtr) ((mtr) & 0x3)
#define MTR_DIMM_COLS_ADDR_BITS(mtr) (MTR_DIMM_COLS(mtr) + 10)
-#ifdef CONFIG_EDAC_DEBUG
-static char *numrow_toString[] = {
- "8,192 - 13 rows",
- "16,384 - 14 rows",
- "32,768 - 15 rows",
- "reserved"
-};
-
-static char *numcol_toString[] = {
- "1,024 - 10 columns",
- "2,048 - 11 columns",
- "4,096 - 12 columns",
- "reserved"
-};
-#endif
-
/* enables the report of miscellaneous messages as CE errors - default off */
static int misc_messages;
@@ -344,7 +328,13 @@ struct i5000_pvt {
struct pci_dev *branch_1; /* 22.0 */
u16 tolm; /* top of low memory */
- u64 ambase; /* AMB BAR */
+ union {
+ u64 ambase; /* AMB BAR */
+ struct {
+ u32 ambase_bottom;
+ u32 ambase_top;
+ } u __packed;
+ };
u16 mir0, mir1, mir2;
@@ -494,10 +484,9 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
ras = NREC_RAS(info->nrecmemb);
cas = NREC_CAS(info->nrecmemb);
- debugf0("\t\tCSROW= %d Channel= %d "
- "(DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
- rank, channel, bank,
- rdwr ? "Write" : "Read", ras, cas);
+ edac_dbg(0, "\t\tCSROW= %d Channel= %d (DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+ rank, channel, bank,
+ rdwr ? "Write" : "Read", ras, cas);
/* Only 1 bit will be on */
switch (allErrors) {
@@ -536,10 +525,10 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
bank, ras, cas, allErrors, specific);
/* Call the helper to output message */
- edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0,
channel >> 1, channel & 1, rank,
rdwr ? "Write error" : "Read error",
- msg, NULL);
+ msg);
}
/*
@@ -574,7 +563,7 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
/* ONLY ONE of the possible error bits will be set, as per the docs */
ue_errors = allErrors & FERR_NF_UNCORRECTABLE;
if (ue_errors) {
- debugf0("\tUncorrected bits= 0x%x\n", ue_errors);
+ edac_dbg(0, "\tUncorrected bits= 0x%x\n", ue_errors);
branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd);
@@ -590,11 +579,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
ras = NREC_RAS(info->nrecmemb);
cas = NREC_CAS(info->nrecmemb);
- debugf0
- ("\t\tCSROW= %d Channels= %d,%d (Branch= %d "
- "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
- rank, channel, channel + 1, branch >> 1, bank,
- rdwr ? "Write" : "Read", ras, cas);
+ edac_dbg(0, "\t\tCSROW= %d Channels= %d,%d (Branch= %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+ rank, channel, channel + 1, branch >> 1, bank,
+ rdwr ? "Write" : "Read", ras, cas);
switch (ue_errors) {
case FERR_NF_M12ERR:
@@ -637,16 +624,16 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
rank, bank, ras, cas, ue_errors, specific);
/* Call the helper to output message */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
channel >> 1, -1, rank,
rdwr ? "Write error" : "Read error",
- msg, NULL);
+ msg);
}
/* Check correctable errors */
ce_errors = allErrors & FERR_NF_CORRECTABLE;
if (ce_errors) {
- debugf0("\tCorrected bits= 0x%x\n", ce_errors);
+ edac_dbg(0, "\tCorrected bits= 0x%x\n", ce_errors);
branch = EXTRACT_FBDCHAN_INDX(info->ferr_nf_fbd);
@@ -664,10 +651,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
ras = REC_RAS(info->recmemb);
cas = REC_CAS(info->recmemb);
- debugf0("\t\tCSROW= %d Channel= %d (Branch %d "
- "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
- rank, channel, branch >> 1, bank,
- rdwr ? "Write" : "Read", ras, cas);
+ edac_dbg(0, "\t\tCSROW= %d Channel= %d (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+ rank, channel, branch >> 1, bank,
+ rdwr ? "Write" : "Read", ras, cas);
switch (ce_errors) {
case FERR_NF_M17ERR:
@@ -692,10 +678,10 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
specific);
/* Call the helper to output message */
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
channel >> 1, channel % 2, rank,
rdwr ? "Write error" : "Read error",
- msg, NULL);
+ msg);
}
if (!misc_messages)
@@ -738,9 +724,9 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
"Err=%#x (%s)", misc_errors, specific);
/* Call the helper to output message */
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
branch >> 1, -1, -1,
- "Misc error", msg, NULL);
+ "Misc error", msg);
}
}
@@ -779,7 +765,7 @@ static void i5000_clear_error(struct mem_ctl_info *mci)
static void i5000_check_error(struct mem_ctl_info *mci)
{
struct i5000_error_info info;
- debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+ edac_dbg(4, "MC%d\n", mci->mc_idx);
i5000_get_error_info(mci, &info);
i5000_process_error_info(mci, &info, 1);
}
@@ -850,15 +836,16 @@ static int i5000_get_devices(struct mem_ctl_info *mci, int dev_idx)
pvt->fsb_error_regs = pdev;
- debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->system_address),
- pvt->system_address->vendor, pvt->system_address->device);
- debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->branchmap_werrors),
- pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device);
- debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->fsb_error_regs),
- pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
+ edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->system_address),
+ pvt->system_address->vendor, pvt->system_address->device);
+ edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->branchmap_werrors),
+ pvt->branchmap_werrors->vendor,
+ pvt->branchmap_werrors->device);
+ edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->fsb_error_regs),
+ pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
pdev = NULL;
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
@@ -981,16 +968,25 @@ static void decode_mtr(int slot_row, u16 mtr)
ans = MTR_DIMMS_PRESENT(mtr);
- debugf2("\tMTR%d=0x%x: DIMMs are %s\n", slot_row, mtr,
- ans ? "Present" : "NOT Present");
+ edac_dbg(2, "\tMTR%d=0x%x: DIMMs are %sPresent\n",
+ slot_row, mtr, ans ? "" : "NOT ");
if (!ans)
return;
- debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
- debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
- debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single");
- debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
- debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
+ edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+ edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+ edac_dbg(2, "\t\tNUMRANK: %s\n",
+ MTR_DIMM_RANK(mtr) ? "double" : "single");
+ edac_dbg(2, "\t\tNUMROW: %s\n",
+ MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+ MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+ MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+ "reserved");
+ edac_dbg(2, "\t\tNUMCOL: %s\n",
+ MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+ MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+ MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+ "reserved");
}
static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
@@ -1061,7 +1057,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
"--------------------------------");
p += n;
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
}
@@ -1082,7 +1078,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
}
p += n;
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
}
@@ -1092,7 +1088,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
"--------------------------------");
p += n;
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
@@ -1105,7 +1101,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
p += n;
space -= n;
}
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
@@ -1118,7 +1114,7 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
}
/* output the last message and free buffer */
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
kfree(mem_buffer);
}
@@ -1141,24 +1137,25 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
pvt = mci->pvt_info;
pci_read_config_dword(pvt->system_address, AMBASE,
- (u32 *) & pvt->ambase);
+ &pvt->u.ambase_bottom);
pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32),
- ((u32 *) & pvt->ambase) + sizeof(u32));
+ &pvt->u.ambase_top);
maxdimmperch = pvt->maxdimmperch;
maxch = pvt->maxch;
- debugf2("AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n",
- (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
+ edac_dbg(2, "AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n",
+ (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
/* Get the Branch Map regs */
pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm);
pvt->tolm >>= 12;
- debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
- pvt->tolm);
+ edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n",
+ pvt->tolm, pvt->tolm);
actual_tolm = pvt->tolm << 28;
- debugf2("Actual TOLM byte addr=%u (0x%x)\n", actual_tolm, actual_tolm);
+ edac_dbg(2, "Actual TOLM byte addr=%u (0x%x)\n",
+ actual_tolm, actual_tolm);
pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0);
pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1);
@@ -1168,15 +1165,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
limit = (pvt->mir0 >> 4) & 0x0FFF;
way0 = pvt->mir0 & 0x1;
way1 = pvt->mir0 & 0x2;
- debugf2("MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
+ edac_dbg(2, "MIR0: limit= 0x%x WAY1= %u WAY0= %x\n",
+ limit, way1, way0);
limit = (pvt->mir1 >> 4) & 0x0FFF;
way0 = pvt->mir1 & 0x1;
way1 = pvt->mir1 & 0x2;
- debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
+ edac_dbg(2, "MIR1: limit= 0x%x WAY1= %u WAY0= %x\n",
+ limit, way1, way0);
limit = (pvt->mir2 >> 4) & 0x0FFF;
way0 = pvt->mir2 & 0x1;
way1 = pvt->mir2 & 0x2;
- debugf2("MIR2: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
+ edac_dbg(2, "MIR2: limit= 0x%x WAY1= %u WAY0= %x\n",
+ limit, way1, way0);
/* Get the MTR[0-3] regs */
for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
@@ -1185,31 +1185,31 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
pci_read_config_word(pvt->branch_0, where,
&pvt->b0_mtr[slot_row]);
- debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where,
- pvt->b0_mtr[slot_row]);
+ edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n",
+ slot_row, where, pvt->b0_mtr[slot_row]);
if (pvt->maxch >= CHANNELS_PER_BRANCH) {
pci_read_config_word(pvt->branch_1, where,
&pvt->b1_mtr[slot_row]);
- debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row,
- where, pvt->b1_mtr[slot_row]);
+ edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n",
+ slot_row, where, pvt->b1_mtr[slot_row]);
} else {
pvt->b1_mtr[slot_row] = 0;
}
}
/* Read and dump branch 0's MTRs */
- debugf2("\nMemory Technology Registers:\n");
- debugf2(" Branch 0:\n");
+ edac_dbg(2, "Memory Technology Registers:\n");
+ edac_dbg(2, " Branch 0:\n");
for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
}
pci_read_config_word(pvt->branch_0, AMB_PRESENT_0,
&pvt->b0_ambpresent0);
- debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
+ edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
pci_read_config_word(pvt->branch_0, AMB_PRESENT_1,
&pvt->b0_ambpresent1);
- debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
+ edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
/* Only if we have 2 branchs (4 channels) */
if (pvt->maxch < CHANNELS_PER_BRANCH) {
@@ -1217,18 +1217,18 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
pvt->b1_ambpresent1 = 0;
} else {
/* Read and dump branch 1's MTRs */
- debugf2(" Branch 1:\n");
+ edac_dbg(2, " Branch 1:\n");
for (slot_row = 0; slot_row < NUM_MTRS; slot_row++) {
decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
}
pci_read_config_word(pvt->branch_1, AMB_PRESENT_0,
&pvt->b1_ambpresent0);
- debugf2("\t\tAMB-Branch 1-present0 0x%x:\n",
- pvt->b1_ambpresent0);
+ edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n",
+ pvt->b1_ambpresent0);
pci_read_config_word(pvt->branch_1, AMB_PRESENT_1,
&pvt->b1_ambpresent1);
- debugf2("\t\tAMB-Branch 1-present1 0x%x:\n",
- pvt->b1_ambpresent1);
+ edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n",
+ pvt->b1_ambpresent1);
}
/* Go and determine the size of each DIMM and place in an
@@ -1363,10 +1363,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
int num_channels;
int num_dimms_per_channel;
- debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n",
- __FILE__, __func__,
- pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
/* We only are looking for func 0 of the set */
if (PCI_FUNC(pdev->devfn) != 0)
@@ -1388,8 +1387,8 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
i5000_get_dimm_and_channel_counts(pdev, &num_dimms_per_channel,
&num_channels);
- debugf0("MC: %s(): Number of Branches=2 Channels= %d DIMMS= %d\n",
- __func__, num_channels, num_dimms_per_channel);
+ edac_dbg(0, "MC: Number of Branches=2 Channels= %d DIMMS= %d\n",
+ num_channels, num_dimms_per_channel);
/* allocate a new MC control structure */
@@ -1406,10 +1405,9 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- kobject_get(&mci->edac_mci_kobj);
- debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
+ edac_dbg(0, "MC: mci = %p\n", mci);
- mci->dev = &pdev->dev; /* record ptr to the generic device */
+ mci->pdev = &pdev->dev; /* record ptr to the generic device */
pvt = mci->pvt_info;
pvt->system_address = pdev; /* Record this device in our private */
@@ -1439,19 +1437,16 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
/* initialize the MC control structure 'csrows' table
* with the mapping and control information */
if (i5000_init_csrows(mci)) {
- debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
- " because i5000_init_csrows() returned nonzero "
- "value\n");
+ edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5000_init_csrows() returned nonzero value\n");
mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
} else {
- debugf1("MC: Enable error reporting now\n");
+ edac_dbg(1, "MC: Enable error reporting now\n");
i5000_enable_error_reporting(mci);
}
/* add this new MC control structure to EDAC's list of MCs */
if (edac_mc_add_mc(mci)) {
- debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n",
- __FILE__, __func__);
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
/* FIXME: perhaps some code should go here that disables error
* reporting if we just enabled it
*/
@@ -1479,7 +1474,6 @@ fail1:
i5000_put_devices(mci);
fail0:
- kobject_put(&mci->edac_mci_kobj);
edac_mc_free(mci);
return -ENODEV;
}
@@ -1496,7 +1490,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "MC:\n");
/* wake up device */
rc = pci_enable_device(pdev);
@@ -1515,7 +1509,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "\n");
if (i5000_pci)
edac_pci_release_generic_ctl(i5000_pci);
@@ -1525,7 +1519,6 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev)
/* retrieve references to resources, and free those resources */
i5000_put_devices(mci);
- kobject_put(&mci->edac_mci_kobj);
edac_mc_free(mci);
}
@@ -1562,7 +1555,7 @@ static int __init i5000_init(void)
{
int pci_rc;
- debugf2("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(2, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -1578,7 +1571,7 @@ static int __init i5000_init(void)
*/
static void __exit i5000_exit(void)
{
- debugf2("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(2, "MC:\n");
pci_unregister_driver(&i5000_driver);
}
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index e9e7c2a29dc3..c4b5e5f868e8 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -431,10 +431,10 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
"bank %u, cas %u, ras %u\n",
bank, cas, ras);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, syndrome,
chan, rank, -1,
- msg, detail, NULL);
+ msg, detail);
}
static void i5100_handle_ue(struct mem_ctl_info *mci,
@@ -453,10 +453,10 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
"bank %u, cas %u, ras %u\n",
bank, cas, ras);
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, syndrome,
chan, rank, -1,
- msg, detail, NULL);
+ msg, detail);
}
static void i5100_read_log(struct mem_ctl_info *mci, int chan,
@@ -859,8 +859,8 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
i5100_rank_to_slot(mci, chan, rank));
}
- debugf2("dimm channel %d, rank %d, size %ld\n",
- chan, rank, (long)PAGES_TO_MiB(npages));
+ edac_dbg(2, "dimm channel %d, rank %d, size %ld\n",
+ chan, rank, (long)PAGES_TO_MiB(npages));
}
}
@@ -943,7 +943,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
goto bail_disable_ch1;
}
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
priv = mci->pvt_info;
priv->ranksperchan = ranksperch;
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 6640c29e1885..277246998b80 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -300,24 +300,6 @@ static inline int extract_fbdchan_indx(u32 x)
return (x>>28) & 0x3;
}
-#ifdef CONFIG_EDAC_DEBUG
-/* MTR NUMROW */
-static const char *numrow_toString[] = {
- "8,192 - 13 rows",
- "16,384 - 14 rows",
- "32,768 - 15 rows",
- "65,536 - 16 rows"
-};
-
-/* MTR NUMCOL */
-static const char *numcol_toString[] = {
- "1,024 - 10 columns",
- "2,048 - 11 columns",
- "4,096 - 12 columns",
- "reserved"
-};
-#endif
-
/* Device name and register DID (Device ID) */
struct i5400_dev_info {
const char *ctl_name; /* name for this device */
@@ -345,7 +327,13 @@ struct i5400_pvt {
struct pci_dev *branch_1; /* 22.0 */
u16 tolm; /* top of low memory */
- u64 ambase; /* AMB BAR */
+ union {
+ u64 ambase; /* AMB BAR */
+ struct {
+ u32 ambase_bottom;
+ u32 ambase_top;
+ } u __packed;
+ };
u16 mir0, mir1;
@@ -560,10 +548,9 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
ras = nrec_ras(info);
cas = nrec_cas(info);
- debugf0("\t\tDIMM= %d Channels= %d,%d (Branch= %d "
- "DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
- rank, channel, channel + 1, branch >> 1, bank,
- buf_id, rdwr_str(rdwr), ras, cas);
+ edac_dbg(0, "\t\tDIMM= %d Channels= %d,%d (Branch= %d DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
+ rank, channel, channel + 1, branch >> 1, bank,
+ buf_id, rdwr_str(rdwr), ras, cas);
/* Only 1 bit will be on */
errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
@@ -573,10 +560,10 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
"Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)",
bank, buf_id, ras, cas, allErrors, error_name[errnum]);
- edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+ edac_mc_handle_error(tp_event, mci, 1, 0, 0, 0,
branch >> 1, -1, rank,
rdwr ? "Write error" : "Read error",
- msg, NULL);
+ msg);
}
/*
@@ -613,7 +600,7 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
/* Correctable errors */
if (allErrors & ERROR_NF_CORRECTABLE) {
- debugf0("\tCorrected bits= 0x%lx\n", allErrors);
+ edac_dbg(0, "\tCorrected bits= 0x%lx\n", allErrors);
branch = extract_fbdchan_indx(info->ferr_nf_fbd);
@@ -634,10 +621,9 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
/* Only 1 bit will be on */
errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
- debugf0("\t\tDIMM= %d Channel= %d (Branch %d "
- "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
- rank, channel, branch >> 1, bank,
- rdwr_str(rdwr), ras, cas);
+ edac_dbg(0, "\t\tDIMM= %d Channel= %d (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+ rank, channel, branch >> 1, bank,
+ rdwr_str(rdwr), ras, cas);
/* Form out message */
snprintf(msg, sizeof(msg),
@@ -646,10 +632,10 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
branch >> 1, bank, rdwr_str(rdwr), ras, cas,
allErrors, error_name[errnum]);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
branch >> 1, channel % 2, rank,
rdwr ? "Write error" : "Read error",
- msg, NULL);
+ msg);
return;
}
@@ -700,7 +686,7 @@ static void i5400_clear_error(struct mem_ctl_info *mci)
static void i5400_check_error(struct mem_ctl_info *mci)
{
struct i5400_error_info info;
- debugf4("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+ edac_dbg(4, "MC%d\n", mci->mc_idx);
i5400_get_error_info(mci, &info);
i5400_process_error_info(mci, &info);
}
@@ -786,15 +772,16 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
}
pvt->fsb_error_regs = pdev;
- debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->system_address),
- pvt->system_address->vendor, pvt->system_address->device);
- debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->branchmap_werrors),
- pvt->branchmap_werrors->vendor, pvt->branchmap_werrors->device);
- debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->fsb_error_regs),
- pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
+ edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->system_address),
+ pvt->system_address->vendor, pvt->system_address->device);
+ edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->branchmap_werrors),
+ pvt->branchmap_werrors->vendor,
+ pvt->branchmap_werrors->device);
+ edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->fsb_error_regs),
+ pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
pvt->branch_0 = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_5400_FBD0, NULL);
@@ -882,8 +869,8 @@ static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel)
n = dimm;
if (n >= DIMMS_PER_CHANNEL) {
- debugf0("ERROR: trying to access an invalid dimm: %d\n",
- dimm);
+ edac_dbg(0, "ERROR: trying to access an invalid dimm: %d\n",
+ dimm);
return 0;
}
@@ -903,20 +890,29 @@ static void decode_mtr(int slot_row, u16 mtr)
ans = MTR_DIMMS_PRESENT(mtr);
- debugf2("\tMTR%d=0x%x: DIMMs are %s\n", slot_row, mtr,
- ans ? "Present" : "NOT Present");
+ edac_dbg(2, "\tMTR%d=0x%x: DIMMs are %sPresent\n",
+ slot_row, mtr, ans ? "" : "NOT ");
if (!ans)
return;
- debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
-
- debugf2("\t\tELECTRICAL THROTTLING is %s\n",
- MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
-
- debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
- debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANK(mtr) ? "double" : "single");
- debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
- debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
+ edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+
+ edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
+ MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
+
+ edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+ edac_dbg(2, "\t\tNUMRANK: %s\n",
+ MTR_DIMM_RANK(mtr) ? "double" : "single");
+ edac_dbg(2, "\t\tNUMROW: %s\n",
+ MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+ MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+ MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+ "65,536 - 16 rows");
+ edac_dbg(2, "\t\tNUMCOL: %s\n",
+ MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+ MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+ MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+ "reserved");
}
static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel,
@@ -989,7 +985,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
"-------------------------------");
p += n;
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
}
@@ -1004,7 +1000,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
p += n;
space -= n;
}
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
}
@@ -1014,7 +1010,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
"-------------------------------");
p += n;
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
@@ -1029,7 +1025,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
}
space -= n;
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
p = mem_buffer;
space = PAGE_SIZE;
@@ -1042,7 +1038,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
}
/* output the last message and free buffer */
- debugf2("%s\n", mem_buffer);
+ edac_dbg(2, "%s\n", mem_buffer);
kfree(mem_buffer);
}
@@ -1065,25 +1061,25 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
pvt = mci->pvt_info;
pci_read_config_dword(pvt->system_address, AMBASE,
- (u32 *) &pvt->ambase);
+ &pvt->u.ambase_bottom);
pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32),
- ((u32 *) &pvt->ambase) + sizeof(u32));
+ &pvt->u.ambase_top);
maxdimmperch = pvt->maxdimmperch;
maxch = pvt->maxch;
- debugf2("AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n",
- (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
+ edac_dbg(2, "AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n",
+ (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
/* Get the Branch Map regs */
pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm);
pvt->tolm >>= 12;
- debugf2("\nTOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
- pvt->tolm);
+ edac_dbg(2, "\nTOLM (number of 256M regions) =%u (0x%x)\n",
+ pvt->tolm, pvt->tolm);
actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
- debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
- actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
+ edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
+ actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0);
pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1);
@@ -1092,11 +1088,13 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
limit = (pvt->mir0 >> 4) & 0x0fff;
way0 = pvt->mir0 & 0x1;
way1 = pvt->mir0 & 0x2;
- debugf2("MIR0: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
+ edac_dbg(2, "MIR0: limit= 0x%x WAY1= %u WAY0= %x\n",
+ limit, way1, way0);
limit = (pvt->mir1 >> 4) & 0xfff;
way0 = pvt->mir1 & 0x1;
way1 = pvt->mir1 & 0x2;
- debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
+ edac_dbg(2, "MIR1: limit= 0x%x WAY1= %u WAY0= %x\n",
+ limit, way1, way0);
/* Get the set of MTR[0-3] regs by each branch */
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) {
@@ -1106,8 +1104,8 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
pci_read_config_word(pvt->branch_0, where,
&pvt->b0_mtr[slot_row]);
- debugf2("MTR%d where=0x%x B0 value=0x%x\n", slot_row, where,
- pvt->b0_mtr[slot_row]);
+ edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n",
+ slot_row, where, pvt->b0_mtr[slot_row]);
if (pvt->maxch < CHANNELS_PER_BRANCH) {
pvt->b1_mtr[slot_row] = 0;
@@ -1117,22 +1115,22 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
/* Branch 1 set of MTR registers */
pci_read_config_word(pvt->branch_1, where,
&pvt->b1_mtr[slot_row]);
- debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row, where,
- pvt->b1_mtr[slot_row]);
+ edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n",
+ slot_row, where, pvt->b1_mtr[slot_row]);
}
/* Read and dump branch 0's MTRs */
- debugf2("\nMemory Technology Registers:\n");
- debugf2(" Branch 0:\n");
+ edac_dbg(2, "Memory Technology Registers:\n");
+ edac_dbg(2, " Branch 0:\n");
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
&pvt->b0_ambpresent0);
- debugf2("\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
+ edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
pci_read_config_word(pvt->branch_0, AMBPRESENT_1,
&pvt->b0_ambpresent1);
- debugf2("\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
+ edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
/* Only if we have 2 branchs (4 channels) */
if (pvt->maxch < CHANNELS_PER_BRANCH) {
@@ -1140,18 +1138,18 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
pvt->b1_ambpresent1 = 0;
} else {
/* Read and dump branch 1's MTRs */
- debugf2(" Branch 1:\n");
+ edac_dbg(2, " Branch 1:\n");
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
&pvt->b1_ambpresent0);
- debugf2("\t\tAMB-Branch 1-present0 0x%x:\n",
- pvt->b1_ambpresent0);
+ edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n",
+ pvt->b1_ambpresent0);
pci_read_config_word(pvt->branch_1, AMBPRESENT_1,
&pvt->b1_ambpresent1);
- debugf2("\t\tAMB-Branch 1-present1 0x%x:\n",
- pvt->b1_ambpresent1);
+ edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n",
+ pvt->b1_ambpresent1);
}
/* Go and determine the size of each DIMM and place in an
@@ -1203,10 +1201,9 @@ static int i5400_init_dimms(struct mem_ctl_info *mci)
size_mb = pvt->dimm_info[slot][channel].megabytes;
- debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n",
- __func__, dimm - mci->dimms,
- channel / 2, channel % 2, slot,
- size_mb / 1000, size_mb % 1000);
+ edac_dbg(2, "dimm (branch %d channel %d slot %d): %d.%03d GB\n",
+ channel / 2, channel % 2, slot,
+ size_mb / 1000, size_mb % 1000);
dimm->nr_pages = size_mb << 8;
dimm->grain = 8;
@@ -1227,7 +1224,7 @@ static int i5400_init_dimms(struct mem_ctl_info *mci)
* With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+.
*/
if (ndimms == 1)
- mci->dimms[0].edac_mode = EDAC_SECDED;
+ mci->dimms[0]->edac_mode = EDAC_SECDED;
return (ndimms == 0);
}
@@ -1270,10 +1267,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
if (dev_idx >= ARRAY_SIZE(i5400_devs))
return -EINVAL;
- debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n",
- __FILE__, __func__,
- pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
/* We only are looking for func 0 of the set */
if (PCI_FUNC(pdev->devfn) != 0)
@@ -1297,9 +1293,9 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
+ edac_dbg(0, "MC: mci = %p\n", mci);
- mci->dev = &pdev->dev; /* record ptr to the generic device */
+ mci->pdev = &pdev->dev; /* record ptr to the generic device */
pvt = mci->pvt_info;
pvt->system_address = pdev; /* Record this device in our private */
@@ -1329,19 +1325,16 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
/* initialize the MC control structure 'dimms' table
* with the mapping and control information */
if (i5400_init_dimms(mci)) {
- debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
- " because i5400_init_dimms() returned nonzero "
- "value\n");
+ edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5400_init_dimms() returned nonzero value\n");
mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */
} else {
- debugf1("MC: Enable error reporting now\n");
+ edac_dbg(1, "MC: Enable error reporting now\n");
i5400_enable_error_reporting(mci);
}
/* add this new MC control structure to EDAC's list of MCs */
if (edac_mc_add_mc(mci)) {
- debugf0("MC: %s: %s(): failed edac_mc_add_mc()\n",
- __FILE__, __func__);
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
/* FIXME: perhaps some code should go here that disables error
* reporting if we just enabled it
*/
@@ -1385,7 +1378,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "MC:\n");
/* wake up device */
rc = pci_enable_device(pdev);
@@ -1404,7 +1397,7 @@ static void __devexit i5400_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "\n");
if (i5400_pci)
edac_pci_release_generic_ctl(i5400_pci);
@@ -1450,7 +1443,7 @@ static int __init i5400_init(void)
{
int pci_rc;
- debugf2("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(2, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -1466,7 +1459,7 @@ static int __init i5400_init(void)
*/
static void __exit i5400_exit(void)
{
- debugf2("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(2, "MC:\n");
pci_unregister_driver(&i5400_driver);
}
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 97c22fd650ee..a09d0667f72a 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -182,24 +182,6 @@ static const u16 mtr_regs[MAX_SLOTS] = {
#define MTR_DIMM_COLS(mtr) ((mtr) & 0x3)
#define MTR_DIMM_COLS_ADDR_BITS(mtr) (MTR_DIMM_COLS(mtr) + 10)
-#ifdef CONFIG_EDAC_DEBUG
-/* MTR NUMROW */
-static const char *numrow_toString[] = {
- "8,192 - 13 rows",
- "16,384 - 14 rows",
- "32,768 - 15 rows",
- "65,536 - 16 rows"
-};
-
-/* MTR NUMCOL */
-static const char *numcol_toString[] = {
- "1,024 - 10 columns",
- "2,048 - 11 columns",
- "4,096 - 12 columns",
- "reserved"
-};
-#endif
-
/************************************************
* i7300 Register definitions for error detection
************************************************/
@@ -467,10 +449,10 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
"Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))",
bank, ras, cas, errors, specific);
- edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 1, 0, 0, 0,
branch, -1, rank,
is_wr ? "Write error" : "Read error",
- pvt->tmp_prt_buffer, NULL);
+ pvt->tmp_prt_buffer);
}
@@ -513,11 +495,11 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
"DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))",
bank, ras, cas, errors, specific);
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0,
syndrome,
branch >> 1, channel % 2, rank,
is_wr ? "Write error" : "Read error",
- pvt->tmp_prt_buffer, NULL);
+ pvt->tmp_prt_buffer);
}
return;
}
@@ -614,9 +596,8 @@ static int decode_mtr(struct i7300_pvt *pvt,
mtr = pvt->mtr[slot][branch];
ans = MTR_DIMMS_PRESENT(mtr) ? 1 : 0;
- debugf2("\tMTR%d CH%d: DIMMs are %s (mtr)\n",
- slot, channel,
- ans ? "Present" : "NOT Present");
+ edac_dbg(2, "\tMTR%d CH%d: DIMMs are %sPresent (mtr)\n",
+ slot, channel, ans ? "" : "NOT ");
/* Determine if there is a DIMM present in this DIMM slot */
if (!ans)
@@ -638,16 +619,25 @@ static int decode_mtr(struct i7300_pvt *pvt,
dinfo->megabytes = 1 << addrBits;
- debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
-
- debugf2("\t\tELECTRICAL THROTTLING is %s\n",
- MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
-
- debugf2("\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
- debugf2("\t\tNUMRANK: %s\n", MTR_DIMM_RANKS(mtr) ? "double" : "single");
- debugf2("\t\tNUMROW: %s\n", numrow_toString[MTR_DIMM_ROWS(mtr)]);
- debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
- debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
+ edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
+
+ edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
+ MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
+
+ edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
+ edac_dbg(2, "\t\tNUMRANK: %s\n",
+ MTR_DIMM_RANKS(mtr) ? "double" : "single");
+ edac_dbg(2, "\t\tNUMROW: %s\n",
+ MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
+ MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
+ MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
+ "65,536 - 16 rows");
+ edac_dbg(2, "\t\tNUMCOL: %s\n",
+ MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
+ MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
+ MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
+ "reserved");
+ edac_dbg(2, "\t\tSIZE: %d MB\n", dinfo->megabytes);
/*
* The type of error detection actually depends of the
@@ -663,9 +653,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
dimm->mtype = MEM_FB_DDR2;
if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
dimm->edac_mode = EDAC_SECDED;
- debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
+ edac_dbg(2, "\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
} else {
- debugf2("\t\tECC code is on Lockstep mode\n");
+ edac_dbg(2, "\t\tECC code is on Lockstep mode\n");
if (MTR_DRAM_WIDTH(mtr) == 8)
dimm->edac_mode = EDAC_S8ECD8ED;
else
@@ -674,9 +664,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
/* ask what device type on this row */
if (MTR_DRAM_WIDTH(mtr) == 8) {
- debugf2("\t\tScrub algorithm for x8 is on %s mode\n",
- IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
- "enhanced" : "normal");
+ edac_dbg(2, "\t\tScrub algorithm for x8 is on %s mode\n",
+ IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
+ "enhanced" : "normal");
dimm->dtype = DEV_X8;
} else
@@ -710,14 +700,14 @@ static void print_dimm_size(struct i7300_pvt *pvt)
p += n;
space -= n;
}
- debugf2("%s\n", pvt->tmp_prt_buffer);
+ edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
p = pvt->tmp_prt_buffer;
space = PAGE_SIZE;
n = snprintf(p, space, "-------------------------------"
"------------------------------");
p += n;
space -= n;
- debugf2("%s\n", pvt->tmp_prt_buffer);
+ edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
p = pvt->tmp_prt_buffer;
space = PAGE_SIZE;
@@ -733,7 +723,7 @@ static void print_dimm_size(struct i7300_pvt *pvt)
space -= n;
}
- debugf2("%s\n", pvt->tmp_prt_buffer);
+ edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
p = pvt->tmp_prt_buffer;
space = PAGE_SIZE;
}
@@ -742,7 +732,7 @@ static void print_dimm_size(struct i7300_pvt *pvt)
"------------------------------");
p += n;
space -= n;
- debugf2("%s\n", pvt->tmp_prt_buffer);
+ edac_dbg(2, "%s\n", pvt->tmp_prt_buffer);
p = pvt->tmp_prt_buffer;
space = PAGE_SIZE;
#endif
@@ -765,7 +755,7 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
pvt = mci->pvt_info;
- debugf2("Memory Technology Registers:\n");
+ edac_dbg(2, "Memory Technology Registers:\n");
/* Get the AMB present registers for the four channels */
for (branch = 0; branch < MAX_BRANCHES; branch++) {
@@ -774,15 +764,15 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
AMBPRESENT_0,
&pvt->ambpresent[channel]);
- debugf2("\t\tAMB-present CH%d = 0x%x:\n",
- channel, pvt->ambpresent[channel]);
+ edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
+ channel, pvt->ambpresent[channel]);
channel = to_channel(1, branch);
pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
AMBPRESENT_1,
&pvt->ambpresent[channel]);
- debugf2("\t\tAMB-present CH%d = 0x%x:\n",
- channel, pvt->ambpresent[channel]);
+ edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n",
+ channel, pvt->ambpresent[channel]);
}
/* Get the set of MTR[0-7] regs by each branch */
@@ -824,12 +814,11 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
static void decode_mir(int mir_no, u16 mir[MAX_MIR])
{
if (mir[mir_no] & 3)
- debugf2("MIR%d: limit= 0x%x Branch(es) that participate:"
- " %s %s\n",
- mir_no,
- (mir[mir_no] >> 4) & 0xfff,
- (mir[mir_no] & 1) ? "B0" : "",
- (mir[mir_no] & 2) ? "B1" : "");
+ edac_dbg(2, "MIR%d: limit= 0x%x Branch(es) that participate: %s %s\n",
+ mir_no,
+ (mir[mir_no] >> 4) & 0xfff,
+ (mir[mir_no] & 1) ? "B0" : "",
+ (mir[mir_no] & 2) ? "B1" : "");
}
/**
@@ -849,17 +838,17 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_dev_16_0_fsb_ctlr, AMBASE,
(u32 *) &pvt->ambase);
- debugf2("AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase);
+ edac_dbg(2, "AMBASE= 0x%lx\n", (long unsigned int)pvt->ambase);
/* Get the Branch Map regs */
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, TOLM, &pvt->tolm);
pvt->tolm >>= 12;
- debugf2("TOLM (number of 256M regions) =%u (0x%x)\n", pvt->tolm,
- pvt->tolm);
+ edac_dbg(2, "TOLM (number of 256M regions) =%u (0x%x)\n",
+ pvt->tolm, pvt->tolm);
actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
- debugf2("Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
- actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
+ edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
+ actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
/* Get memory controller settings */
pci_read_config_dword(pvt->pci_dev_16_1_fsb_addr_map, MC_SETTINGS,
@@ -868,15 +857,15 @@ static int i7300_get_mc_regs(struct mem_ctl_info *mci)
&pvt->mc_settings_a);
if (IS_SINGLE_MODE(pvt->mc_settings_a))
- debugf0("Memory controller operating on single mode\n");
+ edac_dbg(0, "Memory controller operating on single mode\n");
else
- debugf0("Memory controller operating on %s mode\n",
- IS_MIRRORED(pvt->mc_settings) ? "mirrored" : "non-mirrored");
+ edac_dbg(0, "Memory controller operating on %smirrored mode\n",
+ IS_MIRRORED(pvt->mc_settings) ? "" : "non-");
- debugf0("Error detection is %s\n",
- IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
- debugf0("Retry is %s\n",
- IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
+ edac_dbg(0, "Error detection is %s\n",
+ IS_ECC_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
+ edac_dbg(0, "Retry is %s\n",
+ IS_RETRY_ENABLED(pvt->mc_settings) ? "enabled" : "disabled");
/* Get Memory Interleave Range registers */
pci_read_config_word(pvt->pci_dev_16_1_fsb_addr_map, MIR0,
@@ -970,18 +959,18 @@ static int __devinit i7300_get_devices(struct mem_ctl_info *mci)
}
}
- debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->pci_dev_16_0_fsb_ctlr),
- pvt->pci_dev_16_0_fsb_ctlr->vendor,
- pvt->pci_dev_16_0_fsb_ctlr->device);
- debugf1("Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->pci_dev_16_1_fsb_addr_map),
- pvt->pci_dev_16_1_fsb_addr_map->vendor,
- pvt->pci_dev_16_1_fsb_addr_map->device);
- debugf1("FSB Error Regs - PCI Bus ID: %s %x:%x\n",
- pci_name(pvt->pci_dev_16_2_fsb_err_regs),
- pvt->pci_dev_16_2_fsb_err_regs->vendor,
- pvt->pci_dev_16_2_fsb_err_regs->device);
+ edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->pci_dev_16_0_fsb_ctlr),
+ pvt->pci_dev_16_0_fsb_ctlr->vendor,
+ pvt->pci_dev_16_0_fsb_ctlr->device);
+ edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->pci_dev_16_1_fsb_addr_map),
+ pvt->pci_dev_16_1_fsb_addr_map->vendor,
+ pvt->pci_dev_16_1_fsb_addr_map->device);
+ edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s %x:%x\n",
+ pci_name(pvt->pci_dev_16_2_fsb_err_regs),
+ pvt->pci_dev_16_2_fsb_err_regs->vendor,
+ pvt->pci_dev_16_2_fsb_err_regs->device);
pvt->pci_dev_2x_0_fbd_branch[0] = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_I7300_MCH_FB0,
@@ -1032,10 +1021,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
if (rc == -EIO)
return rc;
- debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n",
- __func__,
- pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
/* We only are looking for func 0 of the set */
if (PCI_FUNC(pdev->devfn) != 0)
@@ -1055,9 +1043,9 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
if (mci == NULL)
return -ENOMEM;
- debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
+ edac_dbg(0, "MC: mci = %p\n", mci);
- mci->dev = &pdev->dev; /* record ptr to the generic device */
+ mci->pdev = &pdev->dev; /* record ptr to the generic device */
pvt = mci->pvt_info;
pvt->pci_dev_16_0_fsb_ctlr = pdev; /* Record this device in our private */
@@ -1088,19 +1076,16 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
/* initialize the MC control structure 'csrows' table
* with the mapping and control information */
if (i7300_get_mc_regs(mci)) {
- debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
- " because i7300_init_csrows() returned nonzero "
- "value\n");
+ edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i7300_init_csrows() returned nonzero value\n");
mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
} else {
- debugf1("MC: Enable error reporting now\n");
+ edac_dbg(1, "MC: Enable error reporting now\n");
i7300_enable_error_reporting(mci);
}
/* add this new MC control structure to EDAC's list of MCs */
if (edac_mc_add_mc(mci)) {
- debugf0("MC: " __FILE__
- ": %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
/* FIXME: perhaps some code should go here that disables error
* reporting if we just enabled it
*/
@@ -1142,7 +1127,7 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
char *tmp;
- debugf0(__FILE__ ": %s()\n", __func__);
+ edac_dbg(0, "\n");
if (i7300_pci)
edac_pci_release_generic_ctl(i7300_pci);
@@ -1189,7 +1174,7 @@ static int __init i7300_init(void)
{
int pci_rc;
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -1204,7 +1189,7 @@ static int __init i7300_init(void)
*/
static void __exit i7300_exit(void)
{
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
pci_unregister_driver(&i7300_driver);
}
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index a499c7ed820a..3672101023bd 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -248,6 +248,8 @@ struct i7core_dev {
};
struct i7core_pvt {
+ struct device *addrmatch_dev, *chancounts_dev;
+
struct pci_dev *pci_noncore;
struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
@@ -514,29 +516,28 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
- debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
- pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
- pvt->info.max_dod, pvt->info.ch_map);
+ edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
+ pvt->i7core_dev->socket, pvt->info.mc_control,
+ pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map);
if (ECC_ENABLED(pvt)) {
- debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
+ edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
if (ECCx8(pvt))
mode = EDAC_S8ECD8ED;
else
mode = EDAC_S4ECD4ED;
} else {
- debugf0("ECC disabled\n");
+ edac_dbg(0, "ECC disabled\n");
mode = EDAC_NONE;
}
/* FIXME: need to handle the error codes */
- debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
- "x%x x 0x%x\n",
- numdimms(pvt->info.max_dod),
- numrank(pvt->info.max_dod >> 2),
- numbank(pvt->info.max_dod >> 4),
- numrow(pvt->info.max_dod >> 6),
- numcol(pvt->info.max_dod >> 9));
+ edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n",
+ numdimms(pvt->info.max_dod),
+ numrank(pvt->info.max_dod >> 2),
+ numbank(pvt->info.max_dod >> 4),
+ numrow(pvt->info.max_dod >> 6),
+ numcol(pvt->info.max_dod >> 9));
for (i = 0; i < NUM_CHANS; i++) {
u32 data, dimm_dod[3], value[8];
@@ -545,11 +546,11 @@ static int get_dimm_config(struct mem_ctl_info *mci)
continue;
if (!CH_ACTIVE(pvt, i)) {
- debugf0("Channel %i is not active\n", i);
+ edac_dbg(0, "Channel %i is not active\n", i);
continue;
}
if (CH_DISABLED(pvt, i)) {
- debugf0("Channel %i is disabled\n", i);
+ edac_dbg(0, "Channel %i is disabled\n", i);
continue;
}
@@ -580,15 +581,14 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_ch[i][1],
MC_DOD_CH_DIMM2, &dimm_dod[2]);
- debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
- "%s%s%s%cDIMMs\n",
- i,
- RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
- data,
- pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
- pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
- pvt->channel[i].has_4rank ? "HAS_4R " : "",
- (data & REGISTERED_DIMM) ? 'R' : 'U');
+ edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n",
+ i,
+ RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
+ data,
+ pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
+ pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
+ pvt->channel[i].has_4rank ? "HAS_4R " : "",
+ (data & REGISTERED_DIMM) ? 'R' : 'U');
for (j = 0; j < 3; j++) {
u32 banks, ranks, rows, cols;
@@ -607,11 +607,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
/* DDR3 has 8 I/O banks */
size = (rows * cols * banks * ranks) >> (20 - 3);
- debugf0("\tdimm %d %d Mb offset: %x, "
- "bank: %d, rank: %d, row: %#x, col: %#x\n",
- j, size,
- RANKOFFSET(dimm_dod[j]),
- banks, ranks, rows, cols);
+ edac_dbg(0, "\tdimm %d %d Mb offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
+ j, size,
+ RANKOFFSET(dimm_dod[j]),
+ banks, ranks, rows, cols);
npages = MiB_TO_PAGES(size);
@@ -647,12 +646,12 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
- debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
+ edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
for (j = 0; j < 8; j++)
- debugf1("\t\t%#x\t%#x\t%#x\n",
- (value[j] >> 27) & 0x1,
- (value[j] >> 24) & 0x7,
- (value[j] & ((1 << 24) - 1)));
+ edac_dbg(1, "\t\t%#x\t%#x\t%#x\n",
+ (value[j] >> 27) & 0x1,
+ (value[j] >> 24) & 0x7,
+ (value[j] & ((1 << 24) - 1)));
}
return 0;
@@ -662,6 +661,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
Error insertion routines
****************************************************************************/
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
/* The i7core has independent error injection features per channel.
However, to have a simpler code, we don't allow enabling error injection
on more than one channel.
@@ -691,9 +692,11 @@ static int disable_inject(const struct mem_ctl_info *mci)
* bit 0 - refers to the lower 32-byte half cacheline
* bit 1 - refers to the upper 32-byte half cacheline
*/
-static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
+static ssize_t i7core_inject_section_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
unsigned long value;
int rc;
@@ -709,9 +712,11 @@ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
return count;
}
-static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
- char *data)
+static ssize_t i7core_inject_section_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
return sprintf(data, "0x%08x\n", pvt->inject.section);
}
@@ -724,10 +729,12 @@ static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
* bit 1 - inject ECC error
* bit 2 - inject parity error
*/
-static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
+static ssize_t i7core_inject_type_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
- struct i7core_pvt *pvt = mci->pvt_info;
+ struct mem_ctl_info *mci = to_mci(dev);
+struct i7core_pvt *pvt = mci->pvt_info;
unsigned long value;
int rc;
@@ -742,10 +749,13 @@ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
return count;
}
-static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
- char *data)
+static ssize_t i7core_inject_type_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
+
return sprintf(data, "0x%08x\n", pvt->inject.type);
}
@@ -759,9 +769,11 @@ static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
* 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
* uncorrectable error to be injected.
*/
-static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t i7core_inject_eccmask_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
unsigned long value;
int rc;
@@ -777,10 +789,13 @@ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
return count;
}
-static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
- char *data)
+static ssize_t i7core_inject_eccmask_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
+
return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
}
@@ -797,14 +812,16 @@ static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
#define DECLARE_ADDR_MATCH(param, limit) \
static ssize_t i7core_inject_store_##param( \
- struct mem_ctl_info *mci, \
- const char *data, size_t count) \
+ struct device *dev, \
+ struct device_attribute *mattr, \
+ const char *data, size_t count) \
{ \
+ struct mem_ctl_info *mci = to_mci(dev); \
struct i7core_pvt *pvt; \
long value; \
int rc; \
\
- debugf1("%s()\n", __func__); \
+ edac_dbg(1, "\n"); \
pvt = mci->pvt_info; \
\
if (pvt->inject.enable) \
@@ -824,13 +841,15 @@ static ssize_t i7core_inject_store_##param( \
} \
\
static ssize_t i7core_inject_show_##param( \
- struct mem_ctl_info *mci, \
- char *data) \
+ struct device *dev, \
+ struct device_attribute *mattr, \
+ char *data) \
{ \
+ struct mem_ctl_info *mci = to_mci(dev); \
struct i7core_pvt *pvt; \
\
pvt = mci->pvt_info; \
- debugf1("%s() pvt=%p\n", __func__, pvt); \
+ edac_dbg(1, "pvt=%p\n", pvt); \
if (pvt->inject.param < 0) \
return sprintf(data, "any\n"); \
else \
@@ -838,14 +857,9 @@ static ssize_t i7core_inject_show_##param( \
}
#define ATTR_ADDR_MATCH(param) \
- { \
- .attr = { \
- .name = #param, \
- .mode = (S_IRUGO | S_IWUSR) \
- }, \
- .show = i7core_inject_show_##param, \
- .store = i7core_inject_store_##param, \
- }
+ static DEVICE_ATTR(param, S_IRUGO | S_IWUSR, \
+ i7core_inject_show_##param, \
+ i7core_inject_store_##param)
DECLARE_ADDR_MATCH(channel, 3);
DECLARE_ADDR_MATCH(dimm, 3);
@@ -854,14 +868,21 @@ DECLARE_ADDR_MATCH(bank, 32);
DECLARE_ADDR_MATCH(page, 0x10000);
DECLARE_ADDR_MATCH(col, 0x4000);
+ATTR_ADDR_MATCH(channel);
+ATTR_ADDR_MATCH(dimm);
+ATTR_ADDR_MATCH(rank);
+ATTR_ADDR_MATCH(bank);
+ATTR_ADDR_MATCH(page);
+ATTR_ADDR_MATCH(col);
+
static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
{
u32 read;
int count;
- debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
- dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
- where, val);
+ edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n",
+ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ where, val);
for (count = 0; count < 10; count++) {
if (count)
@@ -899,9 +920,11 @@ static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
* is reliable enough to check if the MC is using the
* three channels. However, this is not clear at the datasheet.
*/
-static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t i7core_inject_enable_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
u32 injectmask;
u64 mask = 0;
@@ -994,17 +1017,18 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
pci_write_config_dword(pvt->pci_noncore,
MC_CFG_CONTROL, 8);
- debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
- " inject 0x%08x\n",
- mask, pvt->inject.eccmask, injectmask);
+ edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
+ mask, pvt->inject.eccmask, injectmask);
return count;
}
-static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
- char *data)
+static ssize_t i7core_inject_enable_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct i7core_pvt *pvt = mci->pvt_info;
u32 injectmask;
@@ -1014,7 +1038,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
MC_CHANNEL_ERROR_INJECT, &injectmask);
- debugf0("Inject error read: 0x%018x\n", injectmask);
+ edac_dbg(0, "Inject error read: 0x%018x\n", injectmask);
if (injectmask & 0x0c)
pvt->inject.enable = 1;
@@ -1024,12 +1048,14 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
#define DECLARE_COUNTER(param) \
static ssize_t i7core_show_counter_##param( \
- struct mem_ctl_info *mci, \
- char *data) \
+ struct device *dev, \
+ struct device_attribute *mattr, \
+ char *data) \
{ \
+ struct mem_ctl_info *mci = to_mci(dev); \
struct i7core_pvt *pvt = mci->pvt_info; \
\
- debugf1("%s() \n", __func__); \
+ edac_dbg(1, "\n"); \
if (!pvt->ce_count_available || (pvt->is_registered)) \
return sprintf(data, "data unavailable\n"); \
return sprintf(data, "%lu\n", \
@@ -1037,121 +1063,179 @@ static ssize_t i7core_show_counter_##param( \
}
#define ATTR_COUNTER(param) \
- { \
- .attr = { \
- .name = __stringify(udimm##param), \
- .mode = (S_IRUGO | S_IWUSR) \
- }, \
- .show = i7core_show_counter_##param \
- }
+ static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR, \
+ i7core_show_counter_##param, \
+ NULL)
DECLARE_COUNTER(0);
DECLARE_COUNTER(1);
DECLARE_COUNTER(2);
+ATTR_COUNTER(0);
+ATTR_COUNTER(1);
+ATTR_COUNTER(2);
+
/*
- * Sysfs struct
+ * inject_addrmatch device sysfs struct
*/
-static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
- ATTR_ADDR_MATCH(channel),
- ATTR_ADDR_MATCH(dimm),
- ATTR_ADDR_MATCH(rank),
- ATTR_ADDR_MATCH(bank),
- ATTR_ADDR_MATCH(page),
- ATTR_ADDR_MATCH(col),
- { } /* End of list */
+static struct attribute *i7core_addrmatch_attrs[] = {
+ &dev_attr_channel.attr,
+ &dev_attr_dimm.attr,
+ &dev_attr_rank.attr,
+ &dev_attr_bank.attr,
+ &dev_attr_page.attr,
+ &dev_attr_col.attr,
+ NULL
};
-static const struct mcidev_sysfs_group i7core_inject_addrmatch = {
- .name = "inject_addrmatch",
- .mcidev_attr = i7core_addrmatch_attrs,
+static struct attribute_group addrmatch_grp = {
+ .attrs = i7core_addrmatch_attrs,
};
-static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
- ATTR_COUNTER(0),
- ATTR_COUNTER(1),
- ATTR_COUNTER(2),
- { .attr = { .name = NULL } }
+static const struct attribute_group *addrmatch_groups[] = {
+ &addrmatch_grp,
+ NULL
};
-static const struct mcidev_sysfs_group i7core_udimm_counters = {
- .name = "all_channel_counts",
- .mcidev_attr = i7core_udimm_counters_attrs,
+static void addrmatch_release(struct device *device)
+{
+ edac_dbg(1, "Releasing device %s\n", dev_name(device));
+ kfree(device);
+}
+
+static struct device_type addrmatch_type = {
+ .groups = addrmatch_groups,
+ .release = addrmatch_release,
};
-static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = {
- {
- .attr = {
- .name = "inject_section",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_section_show,
- .store = i7core_inject_section_store,
- }, {
- .attr = {
- .name = "inject_type",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_type_show,
- .store = i7core_inject_type_store,
- }, {
- .attr = {
- .name = "inject_eccmask",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_eccmask_show,
- .store = i7core_inject_eccmask_store,
- }, {
- .grp = &i7core_inject_addrmatch,
- }, {
- .attr = {
- .name = "inject_enable",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_enable_show,
- .store = i7core_inject_enable_store,
- },
- { } /* End of list */
+/*
+ * all_channel_counts sysfs struct
+ */
+
+static struct attribute *i7core_udimm_counters_attrs[] = {
+ &dev_attr_udimm0.attr,
+ &dev_attr_udimm1.attr,
+ &dev_attr_udimm2.attr,
+ NULL
};
-static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = {
- {
- .attr = {
- .name = "inject_section",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_section_show,
- .store = i7core_inject_section_store,
- }, {
- .attr = {
- .name = "inject_type",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_type_show,
- .store = i7core_inject_type_store,
- }, {
- .attr = {
- .name = "inject_eccmask",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_eccmask_show,
- .store = i7core_inject_eccmask_store,
- }, {
- .grp = &i7core_inject_addrmatch,
- }, {
- .attr = {
- .name = "inject_enable",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = i7core_inject_enable_show,
- .store = i7core_inject_enable_store,
- }, {
- .grp = &i7core_udimm_counters,
- },
- { } /* End of list */
+static struct attribute_group all_channel_counts_grp = {
+ .attrs = i7core_udimm_counters_attrs,
};
+static const struct attribute_group *all_channel_counts_groups[] = {
+ &all_channel_counts_grp,
+ NULL
+};
+
+static void all_channel_counts_release(struct device *device)
+{
+ edac_dbg(1, "Releasing device %s\n", dev_name(device));
+ kfree(device);
+}
+
+static struct device_type all_channel_counts_type = {
+ .groups = all_channel_counts_groups,
+ .release = all_channel_counts_release,
+};
+
+/*
+ * inject sysfs attributes
+ */
+
+static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
+ i7core_inject_section_show, i7core_inject_section_store);
+
+static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR,
+ i7core_inject_type_show, i7core_inject_type_store);
+
+
+static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR,
+ i7core_inject_eccmask_show, i7core_inject_eccmask_store);
+
+static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR,
+ i7core_inject_enable_show, i7core_inject_enable_store);
+
+static int i7core_create_sysfs_devices(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+ int rc;
+
+ rc = device_create_file(&mci->dev, &dev_attr_inject_section);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_type);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_eccmask);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_enable);
+ if (rc < 0)
+ return rc;
+
+ pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL);
+ if (!pvt->addrmatch_dev)
+ return rc;
+
+ pvt->addrmatch_dev->type = &addrmatch_type;
+ pvt->addrmatch_dev->bus = mci->dev.bus;
+ device_initialize(pvt->addrmatch_dev);
+ pvt->addrmatch_dev->parent = &mci->dev;
+ dev_set_name(pvt->addrmatch_dev, "inject_addrmatch");
+ dev_set_drvdata(pvt->addrmatch_dev, mci);
+
+ edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev));
+
+ rc = device_add(pvt->addrmatch_dev);
+ if (rc < 0)
+ return rc;
+
+ if (!pvt->is_registered) {
+ pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev),
+ GFP_KERNEL);
+ if (!pvt->chancounts_dev) {
+ put_device(pvt->addrmatch_dev);
+ device_del(pvt->addrmatch_dev);
+ return rc;
+ }
+
+ pvt->chancounts_dev->type = &all_channel_counts_type;
+ pvt->chancounts_dev->bus = mci->dev.bus;
+ device_initialize(pvt->chancounts_dev);
+ pvt->chancounts_dev->parent = &mci->dev;
+ dev_set_name(pvt->chancounts_dev, "all_channel_counts");
+ dev_set_drvdata(pvt->chancounts_dev, mci);
+
+ edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev));
+
+ rc = device_add(pvt->chancounts_dev);
+ if (rc < 0)
+ return rc;
+ }
+ return 0;
+}
+
+static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci)
+{
+ struct i7core_pvt *pvt = mci->pvt_info;
+
+ edac_dbg(1, "\n");
+
+ device_remove_file(&mci->dev, &dev_attr_inject_section);
+ device_remove_file(&mci->dev, &dev_attr_inject_type);
+ device_remove_file(&mci->dev, &dev_attr_inject_eccmask);
+ device_remove_file(&mci->dev, &dev_attr_inject_enable);
+
+ if (!pvt->is_registered) {
+ put_device(pvt->chancounts_dev);
+ device_del(pvt->chancounts_dev);
+ }
+ put_device(pvt->addrmatch_dev);
+ device_del(pvt->addrmatch_dev);
+}
+
/****************************************************************************
Device initialization routines: put/get, init/exit
****************************************************************************/
@@ -1164,14 +1248,14 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev)
{
int i;
- debugf0(__FILE__ ": %s()\n", __func__);
+ edac_dbg(0, "\n");
for (i = 0; i < i7core_dev->n_devs; i++) {
struct pci_dev *pdev = i7core_dev->pdev[i];
if (!pdev)
continue;
- debugf0("Removing dev %02x:%02x.%d\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ edac_dbg(0, "Removing dev %02x:%02x.%d\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
pci_dev_put(pdev);
}
}
@@ -1214,12 +1298,12 @@ static unsigned i7core_pci_lastbus(void)
while ((b = pci_find_next_bus(b)) != NULL) {
bus = b->number;
- debugf0("Found bus %d\n", bus);
+ edac_dbg(0, "Found bus %d\n", bus);
if (bus > last_bus)
last_bus = bus;
}
- debugf0("Last bus %d\n", last_bus);
+ edac_dbg(0, "Last bus %d\n", last_bus);
return last_bus;
}
@@ -1326,10 +1410,10 @@ static int i7core_get_onedevice(struct pci_dev **prev,
return -ENODEV;
}
- debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
- socket, bus, dev_descr->dev,
- dev_descr->func,
- PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
+ socket, bus, dev_descr->dev,
+ dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
/*
* As stated on drivers/pci/search.c, the reference count for
@@ -1427,13 +1511,13 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
family = "unknown";
pvt->enable_scrub = false;
}
- debugf0("Detected a processor type %s\n", family);
+ edac_dbg(0, "Detected a processor type %s\n", family);
} else
goto error;
- debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
- pdev, i7core_dev->socket);
+ edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n",
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pdev, i7core_dev->socket);
if (PCI_SLOT(pdev->devfn) == 3 &&
PCI_FUNC(pdev->devfn) == 2)
@@ -1452,18 +1536,6 @@ error:
/****************************************************************************
Error check routines
****************************************************************************/
-static void i7core_rdimm_update_errcount(struct mem_ctl_info *mci,
- const int chan,
- const int dimm,
- const int add)
-{
- int i;
-
- for (i = 0; i < add; i++) {
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
- chan, dimm, -1, "error", "", NULL);
- }
-}
static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
const int chan,
@@ -1502,12 +1574,17 @@ static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
/*updated the edac core */
if (add0 != 0)
- i7core_rdimm_update_errcount(mci, chan, 0, add0);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0,
+ 0, 0, 0,
+ chan, 0, -1, "error", "");
if (add1 != 0)
- i7core_rdimm_update_errcount(mci, chan, 1, add1);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1,
+ 0, 0, 0,
+ chan, 1, -1, "error", "");
if (add2 != 0)
- i7core_rdimm_update_errcount(mci, chan, 2, add2);
-
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2,
+ 0, 0, 0,
+ chan, 2, -1, "error", "");
}
static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
@@ -1530,8 +1607,8 @@ static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
&rcv[2][1]);
for (i = 0 ; i < 3; i++) {
- debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
- (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
+ edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
+ (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
/*if the channel has 3 dimms*/
if (pvt->channel[i].dimms > 2) {
new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
@@ -1562,7 +1639,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
int new0, new1, new2;
if (!pvt->pci_mcr[4]) {
- debugf0("%s MCR registers not found\n", __func__);
+ edac_dbg(0, "MCR registers not found\n");
return;
}
@@ -1626,7 +1703,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
const struct mce *m)
{
struct i7core_pvt *pvt = mci->pvt_info;
- char *type, *optype, *err, msg[80];
+ char *type, *optype, *err;
enum hw_event_mc_err_type tp_event;
unsigned long error = m->status & 0x1ff0000l;
bool uncorrected_error = m->mcgstatus & 1ll << 61;
@@ -1704,20 +1781,18 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
err = "unknown";
}
- snprintf(msg, sizeof(msg), "count=%d %s", core_err_cnt, optype);
-
/*
* Call the helper to output message
* FIXME: what to do if core_err_cnt > 1? Currently, it generates
* only one event
*/
if (uncorrected_error || !pvt->is_registered)
- edac_mc_handle_error(tp_event, mci,
+ edac_mc_handle_error(tp_event, mci, core_err_cnt,
m->addr >> PAGE_SHIFT,
m->addr & ~PAGE_MASK,
syndrome,
channel, dimm, -1,
- err, msg, m);
+ err, optype);
}
/*
@@ -2094,8 +2169,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
struct i7core_pvt *pvt;
if (unlikely(!mci || !mci->pvt_info)) {
- debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
- __func__, &i7core_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev);
i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
return;
@@ -2103,8 +2177,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
pvt = mci->pvt_info;
- debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
- __func__, mci, &i7core_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
/* Disable scrubrate setting */
if (pvt->enable_scrub)
@@ -2114,9 +2187,10 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
i7core_pci_ctl_release(pvt);
/* Remove MC sysfs nodes */
- edac_mc_del_mc(mci->dev);
+ i7core_delete_sysfs_devices(mci);
+ edac_mc_del_mc(mci->pdev);
- debugf1("%s: free mci struct\n", mci->ctl_name);
+ edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
kfree(mci->ctl_name);
edac_mc_free(mci);
i7core_dev->mci = NULL;
@@ -2142,8 +2216,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
if (unlikely(!mci))
return -ENOMEM;
- debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
- __func__, mci, &i7core_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt));
@@ -2172,15 +2245,11 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
if (unlikely(rc < 0))
goto fail0;
- if (pvt->is_registered)
- mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs;
- else
- mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs;
/* Get dimm basic config */
get_dimm_config(mci);
/* record ptr to the generic device */
- mci->dev = &i7core_dev->pdev[0]->dev;
+ mci->pdev = &i7core_dev->pdev[0]->dev;
/* Set the function pointer to an actual operation function */
mci->edac_check = i7core_check_error;
@@ -2190,8 +2259,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
/* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
- debugf0("MC: " __FILE__
- ": %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
/* FIXME: perhaps some code should go here that disables error
* reporting if we just enabled it
*/
@@ -2199,6 +2267,12 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
rc = -EINVAL;
goto fail0;
}
+ if (i7core_create_sysfs_devices(mci)) {
+ edac_dbg(0, "MC: failed to create sysfs nodes\n");
+ edac_mc_del_mc(mci->pdev);
+ rc = -EINVAL;
+ goto fail0;
+ }
/* Default error mask is any memory */
pvt->inject.channel = 0;
@@ -2298,7 +2372,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev)
{
struct i7core_dev *i7core_dev;
- debugf0(__FILE__ ": %s()\n", __func__);
+ edac_dbg(0, "\n");
/*
* we have a trouble here: pdev value for removal will be wrong, since
@@ -2347,7 +2421,7 @@ static int __init i7core_init(void)
{
int pci_rc;
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -2374,7 +2448,7 @@ static int __init i7core_init(void)
*/
static void __exit i7core_exit(void)
{
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
pci_unregister_driver(&i7core_driver);
mce_unregister_decode_chain(&i7_mce_dec);
}
diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c
index 52072c28a8a6..90f303db5d1d 100644
--- a/drivers/edac/i82443bxgx_edac.c
+++ b/drivers/edac/i82443bxgx_edac.c
@@ -124,7 +124,7 @@ static void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci,
*info)
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap);
if (info->eap & I82443BXGX_EAP_OFFSET_SBE)
/* Clear error to allow next error to be reported [p.61] */
@@ -156,19 +156,19 @@ static int i82443bxgx_edacmc_process_error_info(struct mem_ctl_info *mci,
if (info->eap & I82443BXGX_EAP_OFFSET_SBE) {
error_found = 1;
if (handle_errors)
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, pageoffset, 0,
edac_mc_find_csrow_by_page(mci, page),
- 0, -1, mci->ctl_name, "", NULL);
+ 0, -1, mci->ctl_name, "");
}
if (info->eap & I82443BXGX_EAP_OFFSET_MBE) {
error_found = 1;
if (handle_errors)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, pageoffset, 0,
edac_mc_find_csrow_by_page(mci, page),
- 0, -1, mci->ctl_name, "", NULL);
+ 0, -1, mci->ctl_name, "");
}
return error_found;
@@ -178,7 +178,7 @@ static void i82443bxgx_edacmc_check(struct mem_ctl_info *mci)
{
struct i82443bxgx_edacmc_error_info info;
- debugf1("MC%d: %s: %s()\n", mci->mc_idx, __FILE__, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i82443bxgx_edacmc_get_error_info(mci, &info);
i82443bxgx_edacmc_process_error_info(mci, &info, 1);
}
@@ -197,18 +197,17 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
row_high_limit_last = 0;
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar);
- debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n",
- mci->mc_idx, __FILE__, __func__, index, drbar);
+ edac_dbg(1, "MC%d: Row=%d DRB = %#0x\n",
+ mci->mc_idx, index, drbar);
row_high_limit = ((u32) drbar << 23);
/* find the DRAM Chip Select Base address and mask */
- debugf1("MC%d: %s: %s() Row=%d, "
- "Boundary Address=%#0x, Last = %#0x\n",
- mci->mc_idx, __FILE__, __func__, index, row_high_limit,
- row_high_limit_last);
+ edac_dbg(1, "MC%d: Row=%d, Boundary Address=%#0x, Last = %#0x\n",
+ mci->mc_idx, index, row_high_limit,
+ row_high_limit_last);
/* 440GX goes to 2GB, represented with a DRB of 0. */
if (row_high_limit_last && !row_high_limit)
@@ -241,7 +240,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
enum mem_type mtype;
enum edac_type edac_mode;
- debugf0("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "MC:\n");
/* Something is really hosed if PCI config space reads from
* the MC aren't working.
@@ -259,8 +258,8 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci);
- mci->dev = &pdev->dev;
+ edac_dbg(0, "MC: mci = %p\n", mci);
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc);
@@ -275,8 +274,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
mtype = MEM_RDR;
break;
default:
- debugf0("Unknown/reserved DRAM type value "
- "in DRAMC register!\n");
+ edac_dbg(0, "Unknown/reserved DRAM type value in DRAMC register!\n");
mtype = -MEM_UNKNOWN;
}
@@ -305,8 +303,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
edac_mode = EDAC_SECDED;
break;
default:
- debugf0("%s(): Unknown/reserved ECC state "
- "in NBXCFG register!\n", __func__);
+ edac_dbg(0, "Unknown/reserved ECC state in NBXCFG register!\n");
edac_mode = EDAC_UNKNOWN;
break;
}
@@ -330,7 +327,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
mci->ctl_page_to_phys = NULL;
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail;
}
@@ -345,7 +342,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
__func__);
}
- debugf3("MC: %s: %s(): success\n", __FILE__, __func__);
+ edac_dbg(3, "MC: success\n");
return 0;
fail:
@@ -361,7 +358,7 @@ static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "MC:\n");
/* don't need to call pci_enable_device() */
rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data);
@@ -376,7 +373,7 @@ static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s: %s()\n", __FILE__, __func__);
+ edac_dbg(0, "\n");
if (i82443bxgx_pci)
edac_pci_release_generic_ctl(i82443bxgx_pci);
@@ -428,7 +425,7 @@ static int __init i82443bxgx_edacmc_init(void)
id = &i82443bxgx_pci_tbl[i];
}
if (!mci_pdev) {
- debugf0("i82443bxgx pci_get_device fail\n");
+ edac_dbg(0, "i82443bxgx pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -436,7 +433,7 @@ static int __init i82443bxgx_edacmc_init(void)
pci_rc = i82443bxgx_edacmc_init_one(mci_pdev, i82443bxgx_pci_tbl);
if (pci_rc < 0) {
- debugf0("i82443bxgx init fail\n");
+ edac_dbg(0, "i82443bxgx init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
index 08045059d10b..1faa74971513 100644
--- a/drivers/edac/i82860_edac.c
+++ b/drivers/edac/i82860_edac.c
@@ -67,7 +67,7 @@ static void i82860_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -109,25 +109,25 @@ static int i82860_process_error_info(struct mem_ctl_info *mci,
return 1;
if ((info->errsts ^ info->errsts2) & 0x0003) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
- -1, -1, -1, "UE overwrote CE", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, "UE overwrote CE", "");
info->errsts = info->errsts2;
}
info->eap >>= PAGE_SHIFT;
row = edac_mc_find_csrow_by_page(mci, info->eap);
- dimm = mci->csrows[row].channels[0].dimm;
+ dimm = mci->csrows[row]->channels[0]->dimm;
if (info->errsts & 0x0002)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
info->eap, 0, 0,
dimm->location[0], dimm->location[1], -1,
- "i82860 UE", "", NULL);
+ "i82860 UE", "");
else
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
info->eap, 0, info->derrsyn,
dimm->location[0], dimm->location[1], -1,
- "i82860 CE", "", NULL);
+ "i82860 CE", "");
return 1;
}
@@ -136,7 +136,7 @@ static void i82860_check(struct mem_ctl_info *mci)
{
struct i82860_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i82860_get_error_info(mci, &info);
i82860_process_error_info(mci, &info, 1);
}
@@ -161,14 +161,13 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
* in all eight rows.
*/
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
cumul_size = (value & I82860_GBA_MASK) <<
(I82860_GBA_SHIFT - PAGE_SHIFT);
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
- cumul_size);
+ edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
if (cumul_size == last_cumul_size)
continue; /* not populated */
@@ -210,8 +209,8 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
if (!mci)
return -ENOMEM;
- debugf3("%s(): init mci\n", __func__);
- mci->dev = &pdev->dev;
+ edac_dbg(3, "init mci\n");
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
/* I"m not sure about this but I think that all RDRAM is SECDED */
@@ -229,7 +228,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail;
}
@@ -245,7 +244,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
@@ -260,7 +259,7 @@ static int __devinit i82860_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
i82860_printk(KERN_INFO, "i82860 init one\n");
if (pci_enable_device(pdev) < 0)
@@ -278,7 +277,7 @@ static void __devexit i82860_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (i82860_pci)
edac_pci_release_generic_ctl(i82860_pci);
@@ -311,7 +310,7 @@ static int __init i82860_init(void)
{
int pci_rc;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -324,7 +323,7 @@ static int __init i82860_init(void)
PCI_DEVICE_ID_INTEL_82860_0, NULL);
if (mci_pdev == NULL) {
- debugf0("860 pci_get_device fail\n");
+ edac_dbg(0, "860 pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -332,7 +331,7 @@ static int __init i82860_init(void)
pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
if (pci_rc < 0) {
- debugf0("860 init fail\n");
+ edac_dbg(0, "860 init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -352,7 +351,7 @@ fail0:
static void __exit i82860_exit(void)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
pci_unregister_driver(&i82860_driver);
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c
index b613e31c16e5..3e416b1a6b53 100644
--- a/drivers/edac/i82875p_edac.c
+++ b/drivers/edac/i82875p_edac.c
@@ -189,7 +189,7 @@ static void i82875p_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -227,7 +227,7 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
{
int row, multi_chan;
- multi_chan = mci->csrows[0].nr_channels - 1;
+ multi_chan = mci->csrows[0]->nr_channels - 1;
if (!(info->errsts & 0x0081))
return 0;
@@ -236,9 +236,9 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
return 1;
if ((info->errsts ^ info->errsts2) & 0x0081) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
-1, -1, -1,
- "UE overwrote CE", "", NULL);
+ "UE overwrote CE", "");
info->errsts = info->errsts2;
}
@@ -246,15 +246,15 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
row = edac_mc_find_csrow_by_page(mci, info->eap);
if (info->errsts & 0x0080)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
info->eap, 0, 0,
row, -1, -1,
- "i82875p UE", "", NULL);
+ "i82875p UE", "");
else
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
info->eap, 0, info->derrsyn,
row, multi_chan ? (info->des & 0x1) : 0,
- -1, "i82875p CE", "", NULL);
+ -1, "i82875p CE", "");
return 1;
}
@@ -263,7 +263,7 @@ static void i82875p_check(struct mem_ctl_info *mci)
{
struct i82875p_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i82875p_get_error_info(mci, &info);
i82875p_process_error_info(mci, &info, 1);
}
@@ -367,12 +367,11 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
*/
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
+ csrow = mci->csrows[index];
value = readb(ovrfl_window + I82875P_DRB + index);
cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT);
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
- cumul_size);
+ edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
if (cumul_size == last_cumul_size)
continue; /* not populated */
@@ -382,7 +381,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
last_cumul_size = cumul_size;
for (j = 0; j < nr_chans; j++) {
- dimm = csrow->channels[j].dimm;
+ dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / nr_chans;
dimm->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */
@@ -405,7 +404,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
u32 nr_chans;
struct i82875p_error_info discard;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL);
@@ -426,11 +425,8 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
goto fail0;
}
- /* Keeps mci available after edac_mc_del_mc() till edac_mc_free() */
- kobject_get(&mci->edac_mci_kobj);
-
- debugf3("%s(): init mci\n", __func__);
- mci->dev = &pdev->dev;
+ edac_dbg(3, "init mci\n");
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_UNKNOWN;
@@ -440,7 +436,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
mci->dev_name = pci_name(pdev);
mci->edac_check = i82875p_check;
mci->ctl_page_to_phys = NULL;
- debugf3("%s(): init pvt\n", __func__);
+ edac_dbg(3, "init pvt\n");
pvt = (struct i82875p_pvt *)mci->pvt_info;
pvt->ovrfl_pdev = ovrfl_pdev;
pvt->ovrfl_window = ovrfl_window;
@@ -451,7 +447,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail1;
}
@@ -467,11 +463,10 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail1:
- kobject_put(&mci->edac_mci_kobj);
edac_mc_free(mci);
fail0:
@@ -489,7 +484,7 @@ static int __devinit i82875p_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
i82875p_printk(KERN_INFO, "i82875p init one\n");
if (pci_enable_device(pdev) < 0)
@@ -508,7 +503,7 @@ static void __devexit i82875p_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
struct i82875p_pvt *pvt = NULL;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (i82875p_pci)
edac_pci_release_generic_ctl(i82875p_pci);
@@ -554,7 +549,7 @@ static int __init i82875p_init(void)
{
int pci_rc;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -569,7 +564,7 @@ static int __init i82875p_init(void)
PCI_DEVICE_ID_INTEL_82875_0, NULL);
if (!mci_pdev) {
- debugf0("875p pci_get_device fail\n");
+ edac_dbg(0, "875p pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -577,7 +572,7 @@ static int __init i82875p_init(void)
pci_rc = i82875p_init_one(mci_pdev, i82875p_pci_tbl);
if (pci_rc < 0) {
- debugf0("875p init fail\n");
+ edac_dbg(0, "875p init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -597,7 +592,7 @@ fail0:
static void __exit i82875p_exit(void)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
i82875p_remove_one(mci_pdev);
pci_dev_put(mci_pdev);
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index 433332c7cdba..069e26c11c4f 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -241,7 +241,7 @@ static void i82975x_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -288,8 +288,8 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
return 1;
if ((info->errsts ^ info->errsts2) & 0x0003) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
- -1, -1, -1, "UE overwrote CE", "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, "UE overwrote CE", "");
info->errsts = info->errsts2;
}
@@ -308,21 +308,21 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
(info->xeap & 1) ? 1 : 0, info->eap, (unsigned int) page);
return 0;
}
- chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1;
+ chan = (mci->csrows[row]->nr_channels == 1) ? 0 : info->eap & 1;
offst = info->eap
& ((1 << PAGE_SHIFT) -
- (1 << mci->csrows[row].channels[chan].dimm->grain));
+ (1 << mci->csrows[row]->channels[chan]->dimm->grain));
if (info->errsts & 0x0002)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, offst, 0,
row, -1, -1,
- "i82975x UE", "", NULL);
+ "i82975x UE", "");
else
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, offst, info->derrsyn,
row, chan ? chan : 0, -1,
- "i82975x CE", "", NULL);
+ "i82975x CE", "");
return 1;
}
@@ -331,7 +331,7 @@ static void i82975x_check(struct mem_ctl_info *mci)
{
struct i82975x_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
i82975x_get_error_info(mci, &info);
i82975x_process_error_info(mci, &info, 1);
}
@@ -394,7 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
*/
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
+ csrow = mci->csrows[index];
value = readb(mch_window + I82975X_DRB + index +
((index >= 4) ? 0x80 : 0));
@@ -406,8 +406,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
*/
if (csrow->nr_channels > 1)
cumul_size <<= 1;
- debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
- cumul_size);
+ edac_dbg(3, "(%d) cumul_size 0x%x\n", index, cumul_size);
nr_pages = cumul_size - last_cumul_size;
if (!nr_pages)
@@ -421,10 +420,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
*/
dtype = i82975x_dram_type(mch_window, index);
for (chan = 0; chan < csrow->nr_channels; chan++) {
- dimm = mci->csrows[index].channels[chan].dimm;
+ dimm = mci->csrows[index]->channels[chan]->dimm;
dimm->nr_pages = nr_pages / csrow->nr_channels;
- strncpy(csrow->channels[chan].dimm->label,
+ strncpy(csrow->channels[chan]->dimm->label,
labels[(index >> 1) + (chan * 2)],
EDAC_MC_LABEL_LEN);
dimm->grain = 1 << 7; /* 128Byte cache-line resolution */
@@ -489,11 +488,11 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
u8 c1drb[4];
#endif
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
pci_read_config_dword(pdev, I82975X_MCHBAR, &mchbar);
if (!(mchbar & 1)) {
- debugf3("%s(): failed, MCHBAR disabled!\n", __func__);
+ edac_dbg(3, "failed, MCHBAR disabled!\n");
goto fail0;
}
mchbar &= 0xffffc000; /* bits 31:14 used for 16K window */
@@ -558,8 +557,8 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
goto fail1;
}
- debugf3("%s(): init mci\n", __func__);
- mci->dev = &pdev->dev;
+ edac_dbg(3, "init mci\n");
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
@@ -569,7 +568,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
mci->dev_name = pci_name(pdev);
mci->edac_check = i82975x_check;
mci->ctl_page_to_phys = NULL;
- debugf3("%s(): init pvt\n", __func__);
+ edac_dbg(3, "init pvt\n");
pvt = (struct i82975x_pvt *) mci->pvt_info;
pvt->mch_window = mch_window;
i82975x_init_csrows(mci, pdev, mch_window);
@@ -578,12 +577,12 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
/* finalize this instance of memory controller with edac core */
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail2;
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail2:
@@ -601,7 +600,7 @@ static int __devinit i82975x_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
@@ -619,7 +618,7 @@ static void __devexit i82975x_remove_one(struct pci_dev *pdev)
struct mem_ctl_info *mci;
struct i82975x_pvt *pvt;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mci = edac_mc_del_mc(&pdev->dev);
if (mci == NULL)
@@ -655,7 +654,7 @@ static int __init i82975x_init(void)
{
int pci_rc;
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -669,7 +668,7 @@ static int __init i82975x_init(void)
PCI_DEVICE_ID_INTEL_82975_0, NULL);
if (!mci_pdev) {
- debugf0("i82975x pci_get_device fail\n");
+ edac_dbg(0, "i82975x pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -677,7 +676,7 @@ static int __init i82975x_init(void)
pci_rc = i82975x_init_one(mci_pdev, i82975x_pci_tbl);
if (pci_rc < 0) {
- debugf0("i82975x init fail\n");
+ edac_dbg(0, "i82975x init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -697,7 +696,7 @@ fail0:
static void __exit i82975x_exit(void)
{
- debugf3("%s()\n", __func__);
+ edac_dbg(3, "\n");
pci_unregister_driver(&i82975x_driver);
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 0e374625f6f8..a1e791ec25d3 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -49,34 +49,45 @@ static u32 orig_hid1[2];
/************************ MC SYSFS parts ***********************************/
-static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci,
+#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
+
+static ssize_t mpc85xx_mc_inject_data_hi_show(struct device *dev,
+ struct device_attribute *mattr,
char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase +
MPC85XX_MC_DATA_ERR_INJECT_HI));
}
-static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_lo_show(struct device *dev,
+ struct device_attribute *mattr,
char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase +
MPC85XX_MC_DATA_ERR_INJECT_LO));
}
-static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data)
+static ssize_t mpc85xx_mc_inject_ctrl_show(struct device *dev,
+ struct device_attribute *mattr,
+ char *data)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT));
}
-static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_hi_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI,
@@ -86,9 +97,11 @@ static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci,
return 0;
}
-static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci,
+static ssize_t mpc85xx_mc_inject_data_lo_store(struct device *dev,
+ struct device_attribute *mattr,
const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO,
@@ -98,9 +111,11 @@ static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci,
return 0;
}
-static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci,
- const char *data, size_t count)
+static ssize_t mpc85xx_mc_inject_ctrl_store(struct device *dev,
+ struct device_attribute *mattr,
+ const char *data, size_t count)
{
+ struct mem_ctl_info *mci = to_mci(dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT,
@@ -110,38 +125,35 @@ static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci,
return 0;
}
-static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = {
- {
- .attr = {
- .name = "inject_data_hi",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = mpc85xx_mc_inject_data_hi_show,
- .store = mpc85xx_mc_inject_data_hi_store},
- {
- .attr = {
- .name = "inject_data_lo",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = mpc85xx_mc_inject_data_lo_show,
- .store = mpc85xx_mc_inject_data_lo_store},
- {
- .attr = {
- .name = "inject_ctrl",
- .mode = (S_IRUGO | S_IWUSR)
- },
- .show = mpc85xx_mc_inject_ctrl_show,
- .store = mpc85xx_mc_inject_ctrl_store},
+DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR,
+ mpc85xx_mc_inject_data_hi_show, mpc85xx_mc_inject_data_hi_store);
+DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR,
+ mpc85xx_mc_inject_data_lo_show, mpc85xx_mc_inject_data_lo_store);
+DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR,
+ mpc85xx_mc_inject_ctrl_show, mpc85xx_mc_inject_ctrl_store);
- /* End of list */
- {
- .attr = {.name = NULL}
- }
-};
+static int mpc85xx_create_sysfs_attributes(struct mem_ctl_info *mci)
+{
+ int rc;
+
+ rc = device_create_file(&mci->dev, &dev_attr_inject_data_hi);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_data_lo);
+ if (rc < 0)
+ return rc;
+ rc = device_create_file(&mci->dev, &dev_attr_inject_ctrl);
+ if (rc < 0)
+ return rc;
-static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
+ return 0;
+}
+
+static void mpc85xx_remove_sysfs_attributes(struct mem_ctl_info *mci)
{
- mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes;
+ device_remove_file(&mci->dev, &dev_attr_inject_data_hi);
+ device_remove_file(&mci->dev, &dev_attr_inject_data_lo);
+ device_remove_file(&mci->dev, &dev_attr_inject_ctrl);
}
/**************************** PCI Err device ***************************/
@@ -268,7 +280,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op)
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
- debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_pci_add_device()\n");
goto err;
}
@@ -291,7 +303,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op)
}
devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
return 0;
@@ -309,7 +321,7 @@ static int mpc85xx_pci_err_remove(struct platform_device *op)
struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev);
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
orig_pci_err_cap_dr);
@@ -570,7 +582,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op)
pdata->edac_idx = edac_dev_idx++;
if (edac_device_add_device(edac_dev) > 0) {
- debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_device_add_device()\n");
goto err;
}
@@ -598,7 +610,7 @@ static int __devinit mpc85xx_l2_err_probe(struct platform_device *op)
devres_remove_group(&op->dev, mpc85xx_l2_err_probe);
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n");
return 0;
@@ -616,7 +628,7 @@ static int mpc85xx_l2_err_remove(struct platform_device *op)
struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev);
struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (edac_op_state == EDAC_OPSTATE_INT) {
out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0);
@@ -813,7 +825,7 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
pfn = err_addr >> PAGE_SHIFT;
for (row_index = 0; row_index < mci->nr_csrows; row_index++) {
- csrow = &mci->csrows[row_index];
+ csrow = mci->csrows[row_index];
if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page))
break;
}
@@ -854,16 +866,16 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
if (err_detect & DDR_EDE_SBE)
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
pfn, err_addr & ~PAGE_MASK, syndrome,
row_index, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
if (err_detect & DDR_EDE_MBE)
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
pfn, err_addr & ~PAGE_MASK, syndrome,
row_index, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
}
@@ -933,8 +945,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
u32 start;
u32 end;
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 +
(index * MPC85XX_MC_CS_BNDS_OFS));
@@ -990,9 +1002,9 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
pdata = mci->pvt_info;
pdata->name = "mpc85xx_mc_err";
pdata->irq = NO_IRQ;
- mci->dev = &op->dev;
+ mci->pdev = &op->dev;
pdata->edac_idx = edac_mc_idx++;
- dev_set_drvdata(mci->dev, mci);
+ dev_set_drvdata(mci->pdev, mci);
mci->ctl_name = pdata->name;
mci->dev_name = pdata->name;
@@ -1026,7 +1038,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
goto err;
}
- debugf3("%s(): init mci\n", __func__);
+ edac_dbg(3, "init mci\n");
mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 |
MEM_FLAG_DDR | MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
@@ -1041,8 +1053,6 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
mci->scrub_mode = SCRUB_SW_SRC;
- mpc85xx_set_mc_sysfs_attributes(mci);
-
mpc85xx_init_csrows(mci);
/* store the original error disable bits */
@@ -1054,7 +1064,13 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0);
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
+ goto err;
+ }
+
+ if (mpc85xx_create_sysfs_attributes(mci)) {
+ edac_mc_del_mc(mci->pdev);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto err;
}
@@ -1088,7 +1104,7 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
}
devres_remove_group(&op->dev, mpc85xx_mc_err_probe);
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
printk(KERN_INFO EDAC_MOD_STR " MC err registered\n");
return 0;
@@ -1106,7 +1122,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op)
struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (edac_op_state == EDAC_OPSTATE_INT) {
out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0);
@@ -1117,6 +1133,7 @@ static int mpc85xx_mc_err_remove(struct platform_device *op)
orig_ddr_err_disable);
out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe);
+ mpc85xx_remove_sysfs_attributes(mci);
edac_mc_del_mc(&op->dev);
edac_mc_free(mci);
return 0;
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index b0bb5a3d2527..2b315c2edc3c 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -169,7 +169,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
MV64X60_PCIx_ERR_MASK_VAL);
if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
- debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_pci_add_device()\n");
goto err;
}
@@ -194,7 +194,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
devres_remove_group(&pdev->dev, mv64x60_pci_err_probe);
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
@@ -210,7 +210,7 @@ static int mv64x60_pci_err_remove(struct platform_device *pdev)
{
struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
edac_pci_del_device(&pdev->dev);
@@ -336,7 +336,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
pdata->edac_idx = edac_dev_idx++;
if (edac_device_add_device(edac_dev) > 0) {
- debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_device_add_device()\n");
goto err;
}
@@ -363,7 +363,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
devres_remove_group(&pdev->dev, mv64x60_sram_err_probe);
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
@@ -379,7 +379,7 @@ static int mv64x60_sram_err_remove(struct platform_device *pdev)
{
struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
edac_device_del_device(&pdev->dev);
edac_device_free_ctl_info(edac_dev);
@@ -531,7 +531,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
pdata->edac_idx = edac_dev_idx++;
if (edac_device_add_device(edac_dev) > 0) {
- debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ edac_dbg(3, "failed edac_device_add_device()\n");
goto err;
}
@@ -558,7 +558,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe);
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
@@ -574,7 +574,7 @@ static int mv64x60_cpu_err_remove(struct platform_device *pdev)
{
struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
edac_device_del_device(&pdev->dev);
edac_device_free_ctl_info(edac_dev);
@@ -611,17 +611,17 @@ static void mv64x60_mc_check(struct mem_ctl_info *mci)
/* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
if (!(reg & 0x1))
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
err_addr >> PAGE_SHIFT,
err_addr & PAGE_MASK, syndrome,
0, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
else /* 2 bit error, UE */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
err_addr >> PAGE_SHIFT,
err_addr & PAGE_MASK, 0,
0, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
/* clear the error */
out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
@@ -670,8 +670,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
- csrow = &mci->csrows[0];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[0];
+ dimm = csrow->channels[0]->dimm;
dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT;
dimm->grain = 8;
@@ -724,7 +724,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
}
pdata = mci->pvt_info;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
platform_set_drvdata(pdev, mci);
pdata->name = "mv64x60_mc_err";
pdata->irq = NO_IRQ;
@@ -766,7 +766,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
goto err2;
}
- debugf3("%s(): init mci\n", __func__);
+ edac_dbg(3, "init mci\n");
mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED;
@@ -790,7 +790,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl);
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto err;
}
@@ -815,7 +815,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
}
/* get this far and it's successful */
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
@@ -831,7 +831,7 @@ static int mv64x60_mc_err_remove(struct platform_device *pdev)
{
struct mem_ctl_info *mci = platform_get_drvdata(pdev);
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
edac_mc_del_mc(&pdev->dev);
edac_mc_free(mci);
diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c
index b095a906a994..2d35b78ada3c 100644
--- a/drivers/edac/pasemi_edac.c
+++ b/drivers/edac/pasemi_edac.c
@@ -74,7 +74,7 @@ static int system_mmc_id;
static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
{
- struct pci_dev *pdev = to_pci_dev(mci->dev);
+ struct pci_dev *pdev = to_pci_dev(mci->pdev);
u32 tmp;
pci_read_config_dword(pdev, MCDEBUG_ERRSTA,
@@ -95,7 +95,7 @@ static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
{
- struct pci_dev *pdev = to_pci_dev(mci->dev);
+ struct pci_dev *pdev = to_pci_dev(mci->pdev);
u32 errlog1a;
u32 cs;
@@ -110,16 +110,16 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
/* uncorrectable/multi-bit errors */
if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS |
MCDEBUG_ERRSTA_RFL_STATUS)) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
- mci->csrows[cs].first_page, 0, 0,
- cs, 0, -1, mci->ctl_name, "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
+ mci->csrows[cs]->first_page, 0, 0,
+ cs, 0, -1, mci->ctl_name, "");
}
/* correctable/single-bit errors */
if (errsta & MCDEBUG_ERRSTA_SBE_STATUS)
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
- mci->csrows[cs].first_page, 0, 0,
- cs, 0, -1, mci->ctl_name, "", NULL);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
+ mci->csrows[cs]->first_page, 0, 0,
+ cs, 0, -1, mci->ctl_name, "");
}
static void pasemi_edac_check(struct mem_ctl_info *mci)
@@ -141,8 +141,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
int index;
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
pci_read_config_dword(pdev,
MCDRAM_RANKCFG + (index * 12),
@@ -225,7 +225,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev,
MCCFG_ERRCOR_ECC_GEN_EN |
MCCFG_ERRCOR_ECC_CRR_EN;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ?
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index f3f9fed06ad7..bf0957635991 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -727,10 +727,10 @@ ppc4xx_edac_handle_ce(struct mem_ctl_info *mci,
for (row = 0; row < mci->nr_csrows; row++)
if (ppc4xx_edac_check_bank_error(status, row))
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, 0,
row, 0, -1,
- message, "", NULL);
+ message, "");
}
/**
@@ -758,10 +758,10 @@ ppc4xx_edac_handle_ue(struct mem_ctl_info *mci,
for (row = 0; row < mci->nr_csrows; row++)
if (ppc4xx_edac_check_bank_error(status, row))
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, offset, 0,
row, 0, -1,
- message, "", NULL);
+ message, "");
}
/**
@@ -1027,9 +1027,9 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
/* Initial driver pointers and private data */
- mci->dev = &op->dev;
+ mci->pdev = &op->dev;
- dev_set_drvdata(mci->dev, mci);
+ dev_set_drvdata(mci->pdev, mci);
pdata = mci->pvt_info;
@@ -1334,7 +1334,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op)
return 0;
fail1:
- edac_mc_del_mc(mci->dev);
+ edac_mc_del_mc(mci->pdev);
fail:
edac_mc_free(mci);
@@ -1368,7 +1368,7 @@ ppc4xx_edac_remove(struct platform_device *op)
dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN);
- edac_mc_del_mc(mci->dev);
+ edac_mc_del_mc(mci->pdev);
edac_mc_free(mci);
return 0;
diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c
index e1cacd164f31..f854debd5533 100644
--- a/drivers/edac/r82600_edac.c
+++ b/drivers/edac/r82600_edac.c
@@ -140,7 +140,7 @@ static void r82600_get_error_info(struct mem_ctl_info *mci,
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
pci_read_config_dword(pdev, R82600_EAP, &info->eapr);
if (info->eapr & BIT(0))
@@ -179,11 +179,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
error_found = 1;
if (handle_errors)
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
page, 0, syndrome,
edac_mc_find_csrow_by_page(mci, page),
0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
if (info->eapr & BIT(1)) { /* UE? */
@@ -191,11 +191,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
if (handle_errors)
/* 82600 doesn't give enough info */
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
page, 0, 0,
edac_mc_find_csrow_by_page(mci, page),
0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
return error_found;
@@ -205,7 +205,7 @@ static void r82600_check(struct mem_ctl_info *mci)
{
struct r82600_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
r82600_get_error_info(mci, &info);
r82600_process_error_info(mci, &info, 1);
}
@@ -230,19 +230,19 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
row_high_limit_last = 0;
for (index = 0; index < mci->nr_csrows; index++) {
- csrow = &mci->csrows[index];
- dimm = csrow->channels[0].dimm;
+ csrow = mci->csrows[index];
+ dimm = csrow->channels[0]->dimm;
/* find the DRAM Chip Select Base address and mask */
pci_read_config_byte(pdev, R82600_DRBA + index, &drbar);
- debugf1("%s() Row=%d DRBA = %#0x\n", __func__, index, drbar);
+ edac_dbg(1, "Row=%d DRBA = %#0x\n", index, drbar);
row_high_limit = ((u32) drbar << 24);
/* row_high_limit = ((u32)drbar << 24) | 0xffffffUL; */
- debugf1("%s() Row=%d, Boundary Address=%#0x, Last = %#0x\n",
- __func__, index, row_high_limit, row_high_limit_last);
+ edac_dbg(1, "Row=%d, Boundary Address=%#0x, Last = %#0x\n",
+ index, row_high_limit, row_high_limit_last);
/* Empty row [p.57] */
if (row_high_limit == row_high_limit_last)
@@ -277,14 +277,13 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
u32 sdram_refresh_rate;
struct r82600_error_info discard;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
pci_read_config_byte(pdev, R82600_DRAMC, &dramcr);
pci_read_config_dword(pdev, R82600_EAP, &eapr);
scrub_disabled = eapr & BIT(31);
sdram_refresh_rate = dramcr & (BIT(0) | BIT(1));
- debugf2("%s(): sdram refresh rate = %#0x\n", __func__,
- sdram_refresh_rate);
- debugf2("%s(): DRAMC register = %#0x\n", __func__, dramcr);
+ edac_dbg(2, "sdram refresh rate = %#0x\n", sdram_refresh_rate);
+ edac_dbg(2, "DRAMC register = %#0x\n", dramcr);
layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
layers[0].size = R82600_NR_CSROWS;
layers[0].is_virt_csrow = true;
@@ -295,8 +294,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
if (mci == NULL)
return -ENOMEM;
- debugf0("%s(): mci = %p\n", __func__, mci);
- mci->dev = &pdev->dev;
+ edac_dbg(0, "mci = %p\n", mci);
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
/* FIXME try to work out if the chip leads have been used for COM2
@@ -311,8 +310,8 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
if (ecc_enabled(dramcr)) {
if (scrub_disabled)
- debugf3("%s(): mci = %p - Scrubbing disabled! EAP: "
- "%#0x\n", __func__, mci, eapr);
+ edac_dbg(3, "mci = %p - Scrubbing disabled! EAP: %#0x\n",
+ mci, eapr);
} else
mci->edac_cap = EDAC_FLAG_NONE;
@@ -329,15 +328,14 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
* type of memory controller. The ID is therefore hardcoded to 0.
*/
if (edac_mc_add_mc(mci)) {
- debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "failed edac_mc_add_mc()\n");
goto fail;
}
/* get this far and it's successful */
if (disable_hardware_scrub) {
- debugf3("%s(): Disabling Hardware Scrub (scrub on error)\n",
- __func__);
+ edac_dbg(3, "Disabling Hardware Scrub (scrub on error)\n");
pci_write_bits32(pdev, R82600_EAP, BIT(31), BIT(31));
}
@@ -352,7 +350,7 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
__func__);
}
- debugf3("%s(): success\n", __func__);
+ edac_dbg(3, "success\n");
return 0;
fail:
@@ -364,7 +362,7 @@ fail:
static int __devinit r82600_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
/* don't need to call pci_enable_device() */
return r82600_probe1(pdev, ent->driver_data);
@@ -374,7 +372,7 @@ static void __devexit r82600_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
if (r82600_pci)
edac_pci_release_generic_ctl(r82600_pci);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 36ad17e79d61..f3b1f9fafa4b 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -381,8 +381,8 @@ static inline int numrank(u32 mtr)
int ranks = (1 << RANK_CNT_BITS(mtr));
if (ranks > 4) {
- debugf0("Invalid number of ranks: %d (max = 4) raw value = %x (%04x)",
- ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr);
+ edac_dbg(0, "Invalid number of ranks: %d (max = 4) raw value = %x (%04x)\n",
+ ranks, (unsigned int)RANK_CNT_BITS(mtr), mtr);
return -EINVAL;
}
@@ -394,8 +394,8 @@ static inline int numrow(u32 mtr)
int rows = (RANK_WIDTH_BITS(mtr) + 12);
if (rows < 13 || rows > 18) {
- debugf0("Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)",
- rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr);
+ edac_dbg(0, "Invalid number of rows: %d (should be between 14 and 17) raw value = %x (%04x)\n",
+ rows, (unsigned int)RANK_WIDTH_BITS(mtr), mtr);
return -EINVAL;
}
@@ -407,8 +407,8 @@ static inline int numcol(u32 mtr)
int cols = (COL_WIDTH_BITS(mtr) + 10);
if (cols > 12) {
- debugf0("Invalid number of cols: %d (max = 4) raw value = %x (%04x)",
- cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr);
+ edac_dbg(0, "Invalid number of cols: %d (max = 4) raw value = %x (%04x)\n",
+ cols, (unsigned int)COL_WIDTH_BITS(mtr), mtr);
return -EINVAL;
}
@@ -475,8 +475,8 @@ static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot,
if (PCI_SLOT(sbridge_dev->pdev[i]->devfn) == slot &&
PCI_FUNC(sbridge_dev->pdev[i]->devfn) == func) {
- debugf1("Associated %02x.%02x.%d with %p\n",
- bus, slot, func, sbridge_dev->pdev[i]);
+ edac_dbg(1, "Associated %02x.%02x.%d with %p\n",
+ bus, slot, func, sbridge_dev->pdev[i]);
return sbridge_dev->pdev[i];
}
}
@@ -523,45 +523,45 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pci_read_config_dword(pvt->pci_br, SAD_CONTROL, &reg);
pvt->sbridge_dev->node_id = NODE_ID(reg);
- debugf0("mc#%d: Node ID: %d, source ID: %d\n",
- pvt->sbridge_dev->mc,
- pvt->sbridge_dev->node_id,
- pvt->sbridge_dev->source_id);
+ edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n",
+ pvt->sbridge_dev->mc,
+ pvt->sbridge_dev->node_id,
+ pvt->sbridge_dev->source_id);
pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
if (IS_MIRROR_ENABLED(reg)) {
- debugf0("Memory mirror is enabled\n");
+ edac_dbg(0, "Memory mirror is enabled\n");
pvt->is_mirrored = true;
} else {
- debugf0("Memory mirror is disabled\n");
+ edac_dbg(0, "Memory mirror is disabled\n");
pvt->is_mirrored = false;
}
pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
- debugf0("Lockstep is enabled\n");
+ edac_dbg(0, "Lockstep is enabled\n");
mode = EDAC_S8ECD8ED;
pvt->is_lockstep = true;
} else {
- debugf0("Lockstep is disabled\n");
+ edac_dbg(0, "Lockstep is disabled\n");
mode = EDAC_S4ECD4ED;
pvt->is_lockstep = false;
}
if (IS_CLOSE_PG(pvt->info.mcmtr)) {
- debugf0("address map is on closed page mode\n");
+ edac_dbg(0, "address map is on closed page mode\n");
pvt->is_close_pg = true;
} else {
- debugf0("address map is on open page mode\n");
+ edac_dbg(0, "address map is on open page mode\n");
pvt->is_close_pg = false;
}
pci_read_config_dword(pvt->pci_ddrio, RANK_CFG_A, &reg);
if (IS_RDIMM_ENABLED(reg)) {
/* FIXME: Can also be LRDIMM */
- debugf0("Memory is registered\n");
+ edac_dbg(0, "Memory is registered\n");
mtype = MEM_RDDR3;
} else {
- debugf0("Memory is unregistered\n");
+ edac_dbg(0, "Memory is unregistered\n");
mtype = MEM_DDR3;
}
@@ -576,7 +576,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
i, j, 0);
pci_read_config_dword(pvt->pci_tad[i],
mtr_regs[j], &mtr);
- debugf4("Channel #%d MTR%d = %x\n", i, j, mtr);
+ edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr);
if (IS_DIMM_PRESENT(mtr)) {
pvt->channel[i].dimms++;
@@ -588,10 +588,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
size = (rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- debugf0("mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
- pvt->sbridge_dev->mc, i, j,
- size, npages,
- banks, ranks, rows, cols);
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ pvt->sbridge_dev->mc, i, j,
+ size, npages,
+ banks, ranks, rows, cols);
dimm->nr_pages = npages;
dimm->grain = 32;
@@ -629,8 +629,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = (1 + pvt->tolm) >> 20;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("TOLM: %u.%03u GB (0x%016Lx)\n",
- mb, kb, (u64)pvt->tolm);
+ edac_dbg(0, "TOLM: %u.%03u GB (0x%016Lx)\n", mb, kb, (u64)pvt->tolm);
/* Address range is already 45:25 */
pci_read_config_dword(pvt->pci_sad1, TOHM,
@@ -639,8 +638,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = (1 + pvt->tohm) >> 20;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("TOHM: %u.%03u GB (0x%016Lx)",
- mb, kb, (u64)pvt->tohm);
+ edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)", mb, kb, (u64)pvt->tohm);
/*
* Step 2) Get SAD range and SAD Interleave list
@@ -663,13 +661,13 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = (limit + 1) >> 20;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("SAD#%d %s up to %u.%03u GB (0x%016Lx) %s reg=0x%08x\n",
- n_sads,
- get_dram_attr(reg),
- mb, kb,
- ((u64)tmp_mb) << 20L,
- INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
- reg);
+ edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n",
+ n_sads,
+ get_dram_attr(reg),
+ mb, kb,
+ ((u64)tmp_mb) << 20L,
+ INTERLEAVE_MODE(reg) ? "8:6" : "[8:6]XOR[18:16]",
+ reg);
prv = limit;
pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
@@ -679,8 +677,8 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
if (j > 0 && sad_interl == sad_pkg(reg, j))
break;
- debugf0("SAD#%d, interleave #%d: %d\n",
- n_sads, j, sad_pkg(reg, j));
+ edac_dbg(0, "SAD#%d, interleave #%d: %d\n",
+ n_sads, j, sad_pkg(reg, j));
}
}
@@ -697,16 +695,16 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = (limit + 1) >> 20;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
- n_tads, mb, kb,
- ((u64)tmp_mb) << 20L,
- (u32)TAD_SOCK(reg),
- (u32)TAD_CH(reg),
- (u32)TAD_TGT0(reg),
- (u32)TAD_TGT1(reg),
- (u32)TAD_TGT2(reg),
- (u32)TAD_TGT3(reg),
- reg);
+ edac_dbg(0, "TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
+ n_tads, mb, kb,
+ ((u64)tmp_mb) << 20L,
+ (u32)TAD_SOCK(reg),
+ (u32)TAD_CH(reg),
+ (u32)TAD_TGT0(reg),
+ (u32)TAD_TGT1(reg),
+ (u32)TAD_TGT2(reg),
+ (u32)TAD_TGT3(reg),
+ reg);
prv = limit;
}
@@ -722,11 +720,11 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
&reg);
tmp_mb = TAD_OFFSET(reg) >> 20;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
- i, j,
- mb, kb,
- ((u64)tmp_mb) << 20L,
- reg);
+ edac_dbg(0, "TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
+ i, j,
+ mb, kb,
+ ((u64)tmp_mb) << 20L,
+ reg);
}
}
@@ -747,12 +745,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = RIR_LIMIT(reg) >> 20;
rir_way = 1 << RIR_WAY(reg);
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
- i, j,
- mb, kb,
- ((u64)tmp_mb) << 20L,
- rir_way,
- reg);
+ edac_dbg(0, "CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
+ i, j,
+ mb, kb,
+ ((u64)tmp_mb) << 20L,
+ rir_way,
+ reg);
for (k = 0; k < rir_way; k++) {
pci_read_config_dword(pvt->pci_tad[i],
@@ -761,12 +759,12 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = RIR_OFFSET(reg) << 6;
mb = div_u64_rem(tmp_mb, 1000, &kb);
- debugf0("CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
- i, j, k,
- mb, kb,
- ((u64)tmp_mb) << 20L,
- (u32)RIR_RNK_TGT(reg),
- reg);
+ edac_dbg(0, "CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
+ i, j, k,
+ mb, kb,
+ ((u64)tmp_mb) << 20L,
+ (u32)RIR_RNK_TGT(reg),
+ reg);
}
}
}
@@ -853,16 +851,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
if (sad_way > 0 && sad_interl == sad_pkg(reg, sad_way))
break;
sad_interleave[sad_way] = sad_pkg(reg, sad_way);
- debugf0("SAD interleave #%d: %d\n",
- sad_way, sad_interleave[sad_way]);
+ edac_dbg(0, "SAD interleave #%d: %d\n",
+ sad_way, sad_interleave[sad_way]);
}
- debugf0("mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n",
- pvt->sbridge_dev->mc,
- n_sads,
- addr,
- limit,
- sad_way + 7,
- interleave_mode ? "" : "XOR[18:16]");
+ edac_dbg(0, "mc#%d: Error detected on SAD#%d: address 0x%016Lx < 0x%016Lx, Interleave [%d:6]%s\n",
+ pvt->sbridge_dev->mc,
+ n_sads,
+ addr,
+ limit,
+ sad_way + 7,
+ interleave_mode ? "" : "XOR[18:16]");
if (interleave_mode)
idx = ((addr >> 6) ^ (addr >> 16)) & 7;
else
@@ -884,8 +882,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
return -EINVAL;
}
*socket = sad_interleave[idx];
- debugf0("SAD interleave index: %d (wayness %d) = CPU socket %d\n",
- idx, sad_way, *socket);
+ edac_dbg(0, "SAD interleave index: %d (wayness %d) = CPU socket %d\n",
+ idx, sad_way, *socket);
/*
* Move to the proper node structure, in order to access the
@@ -972,16 +970,16 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
offset = TAD_OFFSET(tad_offset);
- debugf0("TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n",
- n_tads,
- addr,
- limit,
- (u32)TAD_SOCK(reg),
- ch_way,
- offset,
- idx,
- base_ch,
- *channel_mask);
+ edac_dbg(0, "TAD#%d: address 0x%016Lx < 0x%016Lx, socket interleave %d, channel interleave %d (offset 0x%08Lx), index %d, base ch: %d, ch mask: 0x%02lx\n",
+ n_tads,
+ addr,
+ limit,
+ (u32)TAD_SOCK(reg),
+ ch_way,
+ offset,
+ idx,
+ base_ch,
+ *channel_mask);
/* Calculate channel address */
/* Remove the TAD offset */
@@ -1017,11 +1015,11 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
limit = RIR_LIMIT(reg);
mb = div_u64_rem(limit >> 20, 1000, &kb);
- debugf0("RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
- n_rir,
- mb, kb,
- limit,
- 1 << RIR_WAY(reg));
+ edac_dbg(0, "RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
+ n_rir,
+ mb, kb,
+ limit,
+ 1 << RIR_WAY(reg));
if (ch_addr <= limit)
break;
}
@@ -1042,12 +1040,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
&reg);
*rank = RIR_RNK_TGT(reg);
- debugf0("RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
- n_rir,
- ch_addr,
- limit,
- rir_way,
- idx);
+ edac_dbg(0, "RIR#%d: channel address 0x%08Lx < 0x%08Lx, RIR interleave %d, index %d\n",
+ n_rir,
+ ch_addr,
+ limit,
+ rir_way,
+ idx);
return 0;
}
@@ -1064,14 +1062,14 @@ static void sbridge_put_devices(struct sbridge_dev *sbridge_dev)
{
int i;
- debugf0(__FILE__ ": %s()\n", __func__);
+ edac_dbg(0, "\n");
for (i = 0; i < sbridge_dev->n_devs; i++) {
struct pci_dev *pdev = sbridge_dev->pdev[i];
if (!pdev)
continue;
- debugf0("Removing dev %02x:%02x.%d\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ edac_dbg(0, "Removing dev %02x:%02x.%d\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
pci_dev_put(pdev);
}
}
@@ -1177,10 +1175,9 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
return -ENODEV;
}
- debugf0("Detected dev %02x:%d.%d PCI ID %04x:%04x\n",
- bus, dev_descr->dev,
- dev_descr->func,
- PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
+ edac_dbg(0, "Detected dev %02x:%d.%d PCI ID %04x:%04x\n",
+ bus, dev_descr->dev, dev_descr->func,
+ PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
/*
* As stated on drivers/pci/search.c, the reference count for
@@ -1297,10 +1294,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci,
goto error;
}
- debugf0("Associated PCI %02x.%02d.%d with dev = %p\n",
- sbridge_dev->bus,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
- pdev);
+ edac_dbg(0, "Associated PCI %02x.%02d.%d with dev = %p\n",
+ sbridge_dev->bus,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pdev);
}
/* Check if everything were registered */
@@ -1435,8 +1432,7 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
* to the group of dimm's where the error may be happening.
*/
snprintf(msg, sizeof(msg),
- "count:%d%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
- core_err_cnt,
+ "%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
overflow ? " OVERFLOW" : "",
(uncorrected_error && recoverable) ? " recoverable" : "",
area_type,
@@ -1445,20 +1441,20 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
channel_mask,
rank);
- debugf0("%s", msg);
+ edac_dbg(0, "%s\n", msg);
/* FIXME: need support for channel mask */
/* Call the helper to output message */
- edac_mc_handle_error(tp_event, mci,
+ edac_mc_handle_error(tp_event, mci, core_err_cnt,
m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
channel, dimm, -1,
- optype, msg, m);
+ optype, msg);
return;
err_parsing:
- edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+ edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0,
-1, -1, -1,
- msg, "", m);
+ msg, "");
}
@@ -1592,8 +1588,7 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
struct sbridge_pvt *pvt;
if (unlikely(!mci || !mci->pvt_info)) {
- debugf0("MC: " __FILE__ ": %s(): dev = %p\n",
- __func__, &sbridge_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: dev = %p\n", &sbridge_dev->pdev[0]->dev);
sbridge_printk(KERN_ERR, "Couldn't find mci handler\n");
return;
@@ -1601,13 +1596,13 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
pvt = mci->pvt_info;
- debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
- __func__, mci, &sbridge_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: mci = %p, dev = %p\n",
+ mci, &sbridge_dev->pdev[0]->dev);
/* Remove MC sysfs nodes */
- edac_mc_del_mc(mci->dev);
+ edac_mc_del_mc(mci->pdev);
- debugf1("%s: free mci struct\n", mci->ctl_name);
+ edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
kfree(mci->ctl_name);
edac_mc_free(mci);
sbridge_dev->mci = NULL;
@@ -1638,8 +1633,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
if (unlikely(!mci))
return -ENOMEM;
- debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n",
- __func__, mci, &sbridge_dev->pdev[0]->dev);
+ edac_dbg(0, "MC: mci = %p, dev = %p\n",
+ mci, &sbridge_dev->pdev[0]->dev);
pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt));
@@ -1670,12 +1665,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
get_memory_layout(mci);
/* record ptr to the generic device */
- mci->dev = &sbridge_dev->pdev[0]->dev;
+ mci->pdev = &sbridge_dev->pdev[0]->dev;
/* add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
- debugf0("MC: " __FILE__
- ": %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
rc = -EINVAL;
goto fail0;
}
@@ -1722,7 +1716,8 @@ static int __devinit sbridge_probe(struct pci_dev *pdev,
mc = 0;
list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
- debugf0("Registering MC#%d (%d of %d)\n", mc, mc + 1, num_mc);
+ edac_dbg(0, "Registering MC#%d (%d of %d)\n",
+ mc, mc + 1, num_mc);
sbridge_dev->mc = mc++;
rc = sbridge_register_mci(sbridge_dev);
if (unlikely(rc < 0))
@@ -1752,7 +1747,7 @@ static void __devexit sbridge_remove(struct pci_dev *pdev)
{
struct sbridge_dev *sbridge_dev;
- debugf0(__FILE__ ": %s()\n", __func__);
+ edac_dbg(0, "\n");
/*
* we have a trouble here: pdev value for removal will be wrong, since
@@ -1801,7 +1796,7 @@ static int __init sbridge_init(void)
{
int pci_rc;
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -1825,7 +1820,7 @@ static int __init sbridge_init(void)
*/
static void __exit sbridge_exit(void)
{
- debugf2("MC: " __FILE__ ": %s()\n", __func__);
+ edac_dbg(2, "\n");
pci_unregister_driver(&sbridge_driver);
mce_unregister_decode_chain(&sbridge_mce_dec);
}
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
index 7bb4614730db..1e904b7b79a0 100644
--- a/drivers/edac/tile_edac.c
+++ b/drivers/edac/tile_edac.c
@@ -69,12 +69,12 @@ static void tile_edac_check(struct mem_ctl_info *mci)
/* Check if the current error count is different from the saved one. */
if (mem_error.sbe_count != priv->ce_count) {
- dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node);
+ dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node);
priv->ce_count = mem_error.sbe_count;
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, 0,
0, 0, -1,
- mci->ctl_name, "", NULL);
+ mci->ctl_name, "");
}
}
@@ -84,10 +84,10 @@ static void tile_edac_check(struct mem_ctl_info *mci)
*/
static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
{
- struct csrow_info *csrow = &mci->csrows[0];
+ struct csrow_info *csrow = mci->csrows[0];
struct tile_edac_priv *priv = mci->pvt_info;
struct mshim_mem_info mem_info;
- struct dimm_info *dimm = csrow->channels[0].dimm;
+ struct dimm_info *dimm = csrow->channels[0]->dimm;
if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
@@ -149,7 +149,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev)
priv->node = pdev->id;
priv->hv_devhdl = hv_devhdl;
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_SECDED;
diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c
index 1ac7962d63ea..08a992693e62 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -103,10 +103,10 @@ static int how_many_channel(struct pci_dev *pdev)
pci_read_config_byte(pdev, X38_CAPID0 + 8, &capid0_8b);
if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
- debugf0("In single channel mode.\n");
+ edac_dbg(0, "In single channel mode\n");
x38_channel_num = 1;
} else {
- debugf0("In dual channel mode.\n");
+ edac_dbg(0, "In dual channel mode\n");
x38_channel_num = 2;
}
@@ -151,7 +151,7 @@ static void x38_clear_error_info(struct mem_ctl_info *mci)
{
struct pci_dev *pdev;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* Clear any error bits.
@@ -172,7 +172,7 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci,
struct pci_dev *pdev;
void __iomem *window = mci->pvt_info;
- pdev = to_pci_dev(mci->dev);
+ pdev = to_pci_dev(mci->pdev);
/*
* This is a mess because there is no atomic way to read all the
@@ -215,26 +215,26 @@ static void x38_process_error_info(struct mem_ctl_info *mci,
return;
if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
-1, -1, -1,
- "UE overwrote CE", "", NULL);
+ "UE overwrote CE", "");
info->errsts = info->errsts2;
}
for (channel = 0; channel < x38_channel_num; channel++) {
log = info->eccerrlog[channel];
if (log & X38_ECCERRLOG_UE) {
- edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0, 0, 0,
eccerrlog_row(channel, log),
-1, -1,
- "x38 UE", "", NULL);
+ "x38 UE", "");
} else if (log & X38_ECCERRLOG_CE) {
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0, 0, eccerrlog_syndrome(log),
eccerrlog_row(channel, log),
-1, -1,
- "x38 CE", "", NULL);
+ "x38 CE", "");
}
}
}
@@ -243,7 +243,7 @@ static void x38_check(struct mem_ctl_info *mci)
{
struct x38_error_info info;
- debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
+ edac_dbg(1, "MC%d\n", mci->mc_idx);
x38_get_and_clear_error_info(mci, &info);
x38_process_error_info(mci, &info);
}
@@ -331,7 +331,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
bool stacked;
void __iomem *window;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
window = x38_map_mchbar(pdev);
if (!window)
@@ -352,9 +352,9 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
if (!mci)
return -ENOMEM;
- debugf3("MC: %s(): init mci\n", __func__);
+ edac_dbg(3, "MC: init mci\n");
- mci->dev = &pdev->dev;
+ mci->pdev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR2;
mci->edac_ctl_cap = EDAC_FLAG_SECDED;
@@ -378,7 +378,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
*/
for (i = 0; i < mci->nr_csrows; i++) {
unsigned long nr_pages;
- struct csrow_info *csrow = &mci->csrows[i];
+ struct csrow_info *csrow = mci->csrows[i];
nr_pages = drb_to_nr_pages(drbs, stacked,
i / X38_RANKS_PER_CHANNEL,
@@ -388,7 +388,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
continue;
for (j = 0; j < x38_channel_num; j++) {
- struct dimm_info *dimm = csrow->channels[j].dimm;
+ struct dimm_info *dimm = csrow->channels[j]->dimm;
dimm->nr_pages = nr_pages / x38_channel_num;
dimm->grain = nr_pages << PAGE_SHIFT;
@@ -402,12 +402,12 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
rc = -ENODEV;
if (edac_mc_add_mc(mci)) {
- debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__);
+ edac_dbg(3, "MC: failed edac_mc_add_mc()\n");
goto fail;
}
/* get this far and it's successful */
- debugf3("MC: %s(): success\n", __func__);
+ edac_dbg(3, "MC: success\n");
return 0;
fail:
@@ -423,7 +423,7 @@ static int __devinit x38_init_one(struct pci_dev *pdev,
{
int rc;
- debugf0("MC: %s()\n", __func__);
+ edac_dbg(0, "MC:\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
@@ -439,7 +439,7 @@ static void __devexit x38_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
- debugf0("%s()\n", __func__);
+ edac_dbg(0, "\n");
mci = edac_mc_del_mc(&pdev->dev);
if (!mci)
@@ -472,7 +472,7 @@ static int __init x38_init(void)
{
int pci_rc;
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
@@ -486,14 +486,14 @@ static int __init x38_init(void)
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_X38_HB, NULL);
if (!mci_pdev) {
- debugf0("x38 pci_get_device fail\n");
+ edac_dbg(0, "x38 pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
pci_rc = x38_init_one(mci_pdev, x38_pci_tbl);
if (pci_rc < 0) {
- debugf0("x38 init fail\n");
+ edac_dbg(0, "x38 init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
@@ -513,7 +513,7 @@ fail0:
static void __exit x38_exit(void)
{
- debugf3("MC: %s()\n", __func__);
+ edac_dbg(3, "MC:\n");
pci_unregister_driver(&x38_driver);
if (!x38_registered) {
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 16716356d1fe..e175c8ed4ec4 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -33,7 +33,7 @@ config EXTCON_MAX77693
config EXTCON_MAX8997
tristate "MAX8997 EXTCON Support"
- depends on MFD_MAX8997
+ depends on MFD_MAX8997 && IRQ_DOMAIN
help
If you say yes here you get support for the MUIC device of
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index a4ed30bd9a41..ef9090a4271d 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -26,6 +26,7 @@
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
#include <linux/extcon.h>
+#include <linux/irqdomain.h>
#define DEV_NAME "max8997-muic"
@@ -77,6 +78,7 @@
struct max8997_muic_irq {
unsigned int irq;
const char *name;
+ unsigned int virq;
};
static struct max8997_muic_irq muic_irqs[] = {
@@ -343,12 +345,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
{
struct max8997_muic_info *info = container_of(work,
struct max8997_muic_info, irq_work);
- struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
u8 status[2];
u8 adc, chg_type;
-
- int irq_type = info->irq - max8997->irq_base;
- int ret;
+ int irq_type = 0;
+ int i, ret;
mutex_lock(&info->mutex);
@@ -363,6 +363,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
status[0], status[1]);
+ for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+ if (info->irq == muic_irqs[i].virq)
+ irq_type = muic_irqs[i].irq;
+
switch (irq_type) {
case MAX8997_MUICIRQ_ADC:
adc = status[0] & STATUS1_ADC_MASK;
@@ -448,11 +452,15 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
struct max8997_muic_irq *muic_irq = &muic_irqs[i];
+ int virq = 0;
+
+ virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
+ if (!virq)
+ goto err_irq;
+ muic_irq->virq = virq;
- ret = request_threaded_irq(pdata->irq_base + muic_irq->irq,
- NULL, max8997_muic_irq_handler,
- 0, muic_irq->name,
- info);
+ ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler,
+ 0, muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
@@ -496,7 +504,7 @@ err_extcon:
kfree(info->edev);
err_irq:
while (--i >= 0)
- free_irq(pdata->irq_base + muic_irqs[i].irq, info);
+ free_irq(muic_irqs[i].virq, info);
kfree(info);
err_kfree:
return ret;
@@ -505,11 +513,10 @@ err_kfree:
static int __devexit max8997_muic_remove(struct platform_device *pdev)
{
struct max8997_muic_info *info = platform_get_drvdata(pdev);
- struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
int i;
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
- free_irq(max8997->irq_base + muic_irqs[i].irq, info);
+ free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index 4d460ef87161..7a05fd24d68b 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -398,6 +398,14 @@ static ssize_t guid_show(struct device *dev,
return ret;
}
+static ssize_t is_local_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fw_device *device = fw_device(dev);
+
+ return sprintf(buf, "%u\n", device->is_local);
+}
+
static int units_sprintf(char *buf, const u32 *directory)
{
struct fw_csr_iterator ci;
@@ -447,6 +455,7 @@ static ssize_t units_show(struct device *dev,
static struct device_attribute fw_device_attributes[] = {
__ATTR_RO(config_rom),
__ATTR_RO(guid),
+ __ATTR_RO(is_local),
__ATTR_RO(units),
__ATTR_NULL,
};
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index 8382e27e9a27..38c0aa60b2cb 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -146,7 +146,7 @@ EXPORT_SYMBOL(fw_iso_buffer_destroy);
/* Convert DMA address to offset into virtually contiguous buffer. */
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
{
- int i;
+ size_t i;
dma_addr_t address;
ssize_t offset;
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 780708dc6e25..87d6f2d2f02d 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -525,9 +525,10 @@ const struct fw_address_region fw_high_memory_region =
{ .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, };
EXPORT_SYMBOL(fw_high_memory_region);
-#if 0
-const struct fw_address_region fw_low_memory_region =
+static const struct fw_address_region low_memory_region =
{ .start = 0x000000000000ULL, .end = 0x000100000000ULL, };
+
+#if 0
const struct fw_address_region fw_private_region =
{ .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, };
const struct fw_address_region fw_csr_region =
@@ -1198,6 +1199,23 @@ static struct fw_address_handler registers = {
.address_callback = handle_registers,
};
+static void handle_low_memory(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *payload, size_t length,
+ void *callback_data)
+{
+ /*
+ * This catches requests not handled by the physical DMA unit,
+ * i.e., wrong transaction types or unauthorized source nodes.
+ */
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+}
+
+static struct fw_address_handler low_memory = {
+ .length = 0x000100000000ULL,
+ .address_callback = handle_low_memory,
+};
+
MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");
MODULE_DESCRIPTION("Core IEEE1394 transaction logic");
MODULE_LICENSE("GPL");
@@ -1259,6 +1277,7 @@ static int __init fw_core_init(void)
fw_core_add_address_handler(&topology_map, &topology_map_region);
fw_core_add_address_handler(&registers, &registers_region);
+ fw_core_add_address_handler(&low_memory, &low_memory_region);
fw_core_add_descriptor(&vendor_id_descriptor);
fw_core_add_descriptor(&model_id_descriptor);
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index c1af05e834b6..c788dbdaf3bc 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -191,6 +191,7 @@ struct fw_ohci {
unsigned quirks;
unsigned int pri_req_max;
u32 bus_time;
+ bool bus_time_running;
bool is_root;
bool csr_state_setclear_abdicate;
int n_ir;
@@ -1726,6 +1727,13 @@ static u32 update_bus_time(struct fw_ohci *ohci)
{
u32 cycle_time_seconds = get_cycle_time(ohci) >> 25;
+ if (unlikely(!ohci->bus_time_running)) {
+ reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_cycle64Seconds);
+ ohci->bus_time = (lower_32_bits(get_seconds()) & ~0x7f) |
+ (cycle_time_seconds & 0x40);
+ ohci->bus_time_running = true;
+ }
+
if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40))
ohci->bus_time += 0x40;
@@ -2213,7 +2221,7 @@ static int ohci_enable(struct fw_card *card,
{
struct fw_ohci *ohci = fw_ohci(card);
struct pci_dev *dev = to_pci_dev(card->device);
- u32 lps, seconds, version, irqs;
+ u32 lps, version, irqs;
int i, ret;
if (software_reset(ohci)) {
@@ -2269,9 +2277,12 @@ static int ohci_enable(struct fw_card *card,
(OHCI1394_MAX_PHYS_RESP_RETRIES << 8) |
(200 << 16));
- seconds = lower_32_bits(get_seconds());
- reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25);
- ohci->bus_time = seconds & ~0x3f;
+ ohci->bus_time_running = false;
+
+ for (i = 0; i < 32; i++)
+ if (ohci->ir_context_support & (1 << i))
+ reg_write(ohci, OHCI1394_IsoRcvContextControlClear(i),
+ IR_CONTEXT_MULTI_CHANNEL_MODE);
version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
if (version >= OHCI_VERSION_1_1) {
@@ -2369,7 +2380,6 @@ static int ohci_enable(struct fw_card *card,
OHCI1394_postedWriteErr |
OHCI1394_selfIDComplete |
OHCI1394_regAccessFail |
- OHCI1394_cycle64Seconds |
OHCI1394_cycleInconsistent |
OHCI1394_unrecoverableError |
OHCI1394_cycleTooLong |
@@ -2658,7 +2668,8 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value)
case CSR_BUS_TIME:
spin_lock_irqsave(&ohci->lock, flags);
- ohci->bus_time = (ohci->bus_time & 0x7f) | (value & ~0x7f);
+ ohci->bus_time = (update_bus_time(ohci) & 0x40) |
+ (value & ~0x7f);
spin_unlock_irqrestore(&ohci->lock, flags);
break;
@@ -3539,6 +3550,13 @@ static int __devinit pci_probe(struct pci_dev *dev,
INIT_WORK(&ohci->bus_reset_work, bus_reset_work);
+ if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM) ||
+ pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) {
+ dev_err(&dev->dev, "invalid MMIO resource\n");
+ err = -ENXIO;
+ goto fail_disable;
+ }
+
err = pci_request_region(dev, 0, ohci_driver_name);
if (err) {
dev_err(&dev->dev, "MMIO resource unavailable\n");
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 153980be4ee6..b298158cb922 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -6,6 +6,7 @@
#include <linux/dmi.h>
#include <linux/efi.h>
#include <linux/bootmem.h>
+#include <linux/random.h>
#include <asm/dmi.h>
/*
@@ -111,6 +112,8 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
dmi_table(buf, dmi_len, dmi_num, decode, NULL);
+ add_device_randomness(buf, dmi_len);
+
dmi_iounmap(buf, dmi_len);
return 0;
}
diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c
index adc07102a20d..c1cdc9236666 100644
--- a/drivers/firmware/memmap.c
+++ b/drivers/firmware/memmap.c
@@ -98,7 +98,7 @@ static LIST_HEAD(map_entries);
/**
* firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
* @start: Start of the memory range.
- * @end: End of the memory range (inclusive).
+ * @end: End of the memory range (exclusive).
* @type: Type of the memory range.
* @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised
* entry.
@@ -113,7 +113,7 @@ static int firmware_map_add_entry(u64 start, u64 end,
BUG_ON(start > end);
entry->start = start;
- entry->end = end;
+ entry->end = end - 1;
entry->type = type;
INIT_LIST_HEAD(&entry->list);
kobject_init(&entry->kobj, &memmap_ktype);
@@ -148,7 +148,7 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
* firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
* memory hotplug.
* @start: Start of the memory range.
- * @end: End of the memory range (inclusive).
+ * @end: End of the memory range (exclusive)
* @type: Type of the memory range.
*
* Adds a firmware mapping entry. This function is for memory hotplug, it is
@@ -175,7 +175,7 @@ int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
/**
* firmware_map_add_early() - Adds a firmware mapping entry.
* @start: Start of the memory range.
- * @end: End of the memory range (inclusive).
+ * @end: End of the memory range.
* @type: Type of the memory range.
*
* Adds a firmware mapping entry. This function uses the bootmem allocator
diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c
index 51e0e2d8fac6..a330492e06f9 100644
--- a/drivers/firmware/pcdp.c
+++ b/drivers/firmware/pcdp.c
@@ -95,7 +95,7 @@ efi_setup_pcdp_console(char *cmdline)
if (efi.hcdp == EFI_INVALID_TABLE_ADDR)
return -ENODEV;
- pcdp = ioremap(efi.hcdp, 4096);
+ pcdp = early_ioremap(efi.hcdp, 4096);
printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp);
if (strstr(cmdline, "console=hcdp")) {
@@ -131,6 +131,6 @@ efi_setup_pcdp_console(char *cmdline)
}
out:
- iounmap(pcdp);
+ early_iounmap(pcdp, 4096);
return rc;
}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 502b5ea43f4f..b16c8a72a2e2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -597,6 +597,13 @@ config GPIO_AB8500
help
Select this to enable the AB8500 IC GPIO driver
+config GPIO_TPS6586X
+ bool "TPS6586X GPIO"
+ depends on MFD_TPS6586X
+ help
+ Select this option to enable GPIO driver for the TPS6586X
+ chip family.
+
config GPIO_TPS65910
bool "TPS65910 GPIO"
depends on MFD_TPS65910
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d37048105a87..153caceeb053 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
+obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
new file mode 100644
index 000000000000..2526b3bb0fae
--- /dev/null
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -0,0 +1,158 @@
+/*
+ * TI TPS6586x GPIO driver
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on tps6586x.c
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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/errno.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1 0x5d
+#define TPS6586X_GPIOSET2 0x5e
+
+struct tps6586x_gpio {
+ struct gpio_chip gpio_chip;
+ struct device *parent;
+};
+
+static inline struct tps6586x_gpio *to_tps6586x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tps6586x_gpio, gpio_chip);
+}
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+ uint8_t val;
+ int ret;
+
+ ret = tps6586x_read(tps6586x_gpio->parent, TPS6586X_GPIOSET2, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & (1 << offset));
+}
+
+static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+
+ tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2,
+ value << offset, 1 << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
+ uint8_t val, mask;
+
+ tps6586x_gpio_set(gc, offset, value);
+
+ val = 0x1 << (offset * 2);
+ mask = 0x3 << (offset * 2);
+
+ return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET1,
+ val, mask);
+}
+
+static int __devinit tps6586x_gpio_probe(struct platform_device *pdev)
+{
+ struct tps6586x_platform_data *pdata;
+ struct tps6586x_gpio *tps6586x_gpio;
+ int ret;
+
+ pdata = dev_get_platdata(pdev->dev.parent);
+ tps6586x_gpio = devm_kzalloc(&pdev->dev,
+ sizeof(*tps6586x_gpio), GFP_KERNEL);
+ if (!tps6586x_gpio) {
+ dev_err(&pdev->dev, "Could not allocate tps6586x_gpio\n");
+ return -ENOMEM;
+ }
+
+ tps6586x_gpio->parent = pdev->dev.parent;
+
+ tps6586x_gpio->gpio_chip.owner = THIS_MODULE;
+ tps6586x_gpio->gpio_chip.label = pdev->name;
+ tps6586x_gpio->gpio_chip.dev = &pdev->dev;
+ tps6586x_gpio->gpio_chip.ngpio = 4;
+ tps6586x_gpio->gpio_chip.can_sleep = 1;
+
+ /* FIXME: add handling of GPIOs as dedicated inputs */
+ tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output;
+ tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set;
+ tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get;
+
+#ifdef CONFIG_OF_GPIO
+ tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node;
+#endif
+ if (pdata && pdata->gpio_base)
+ tps6586x_gpio->gpio_chip.base = pdata->gpio_base;
+ else
+ tps6586x_gpio->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&tps6586x_gpio->gpio_chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, tps6586x_gpio);
+
+ return ret;
+}
+
+static int __devexit tps6586x_gpio_remove(struct platform_device *pdev)
+{
+ struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev);
+
+ return gpiochip_remove(&tps6586x_gpio->gpio_chip);
+}
+
+static struct platform_driver tps6586x_gpio_driver = {
+ .driver.name = "tps6586x-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = tps6586x_gpio_probe,
+ .remove = __devexit_p(tps6586x_gpio_remove),
+};
+
+static int __init tps6586x_gpio_init(void)
+{
+ return platform_driver_register(&tps6586x_gpio_driver);
+}
+subsys_initcall(tps6586x_gpio_init);
+
+static void __exit tps6586x_gpio_exit(void)
+{
+ platform_driver_unregister(&tps6586x_gpio_driver);
+}
+module_exit(tps6586x_gpio_exit);
+
+MODULE_ALIAS("platform:tps6586x-gpio");
+MODULE_DESCRIPTION("GPIO interface for TPS6586X PMIC");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index bf791fa0e50d..d9568198c300 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -196,7 +196,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
return ret;
}
-struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
+static struct drm_encoder *exynos_drm_best_encoder(
+ struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index eaf630dc5dba..84dd099eae3b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -33,7 +33,6 @@
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
-static struct drm_device *drm_dev;
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
@@ -120,8 +119,6 @@ int exynos_drm_device_register(struct drm_device *dev)
if (!dev)
return -EINVAL;
- drm_dev = dev;
-
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
subdrv->drm_dev = dev;
err = exynos_drm_subdrv_probe(dev, subdrv);
@@ -149,8 +146,6 @@ int exynos_drm_device_unregister(struct drm_device *dev)
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
exynos_drm_subdrv_remove(dev, subdrv);
- drm_dev = NULL;
-
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 32a34c85899b..abb1e2f8227f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -29,21 +29,23 @@
#include "drmP.h"
#include "drm_crtc_helper.h"
-#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
-#include "exynos_drm_fb.h"
#include "exynos_drm_encoder.h"
-#include "exynos_drm_gem.h"
+#include "exynos_drm_plane.h"
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc)
+enum exynos_crtc_mode {
+ CRTC_MODE_NORMAL, /* normal mode */
+ CRTC_MODE_BLANK, /* The private plane of crtc is blank */
+};
+
/*
* Exynos specific crtc structure.
*
* @drm_crtc: crtc object.
- * @overlay: contain information common to display controller and hdmi and
- * contents of this overlay object would be copied to sub driver size.
+ * @drm_plane: pointer of private plane object for this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
@@ -52,115 +54,16 @@
* we can refer to the crtc to current hardware interrupt occured through
* this pipe value.
* @dpms: store the crtc dpms value
+ * @mode: store the crtc mode value
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
- struct exynos_drm_overlay overlay;
+ struct drm_plane *plane;
unsigned int pipe;
unsigned int dpms;
+ enum exynos_crtc_mode mode;
};
-static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
-{
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
-
- exynos_drm_fn_encoder(crtc, overlay,
- exynos_drm_encoder_crtc_mode_set);
- exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
- exynos_drm_encoder_crtc_commit);
-}
-
-int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
- struct drm_framebuffer *fb,
- struct drm_display_mode *mode,
- struct exynos_drm_crtc_pos *pos)
-{
- struct exynos_drm_gem_buf *buffer;
- unsigned int actual_w;
- unsigned int actual_h;
- int nr = exynos_drm_format_num_buffers(fb->pixel_format);
- int i;
-
- for (i = 0; i < nr; i++) {
- buffer = exynos_drm_fb_buffer(fb, i);
- if (!buffer) {
- DRM_LOG_KMS("buffer is null\n");
- return -EFAULT;
- }
-
- overlay->dma_addr[i] = buffer->dma_addr;
- overlay->vaddr[i] = buffer->kvaddr;
-
- DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
- i, (unsigned long)overlay->vaddr[i],
- (unsigned long)overlay->dma_addr[i]);
- }
-
- actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
- actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
-
- /* set drm framebuffer data. */
- overlay->fb_x = pos->fb_x;
- overlay->fb_y = pos->fb_y;
- overlay->fb_width = fb->width;
- overlay->fb_height = fb->height;
- overlay->src_width = pos->src_w;
- overlay->src_height = pos->src_h;
- overlay->bpp = fb->bits_per_pixel;
- overlay->pitch = fb->pitches[0];
- overlay->pixel_format = fb->pixel_format;
-
- /* set overlay range to be displayed. */
- overlay->crtc_x = pos->crtc_x;
- overlay->crtc_y = pos->crtc_y;
- overlay->crtc_width = actual_w;
- overlay->crtc_height = actual_h;
-
- /* set drm mode data. */
- overlay->mode_width = mode->hdisplay;
- overlay->mode_height = mode->vdisplay;
- overlay->refresh = mode->vrefresh;
- overlay->scan_flag = mode->flags;
-
- DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
- overlay->crtc_x, overlay->crtc_y,
- overlay->crtc_width, overlay->crtc_height);
-
- return 0;
-}
-
-static int exynos_drm_crtc_update(struct drm_crtc *crtc)
-{
- struct exynos_drm_crtc *exynos_crtc;
- struct exynos_drm_overlay *overlay;
- struct exynos_drm_crtc_pos pos;
- struct drm_display_mode *mode = &crtc->mode;
- struct drm_framebuffer *fb = crtc->fb;
-
- if (!mode || !fb)
- return -EINVAL;
-
- exynos_crtc = to_exynos_crtc(crtc);
- overlay = &exynos_crtc->overlay;
-
- memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
-
- /* it means the offset of framebuffer to be displayed. */
- pos.fb_x = crtc->x;
- pos.fb_y = crtc->y;
-
- /* OSD position to be displayed. */
- pos.crtc_x = 0;
- pos.crtc_y = 0;
- pos.crtc_w = fb->width - crtc->x;
- pos.crtc_h = fb->height - crtc->y;
- pos.src_w = pos.crtc_w;
- pos.src_h = pos.crtc_h;
-
- return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos);
-}
-
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
@@ -175,23 +78,8 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
mutex_lock(&dev->struct_mutex);
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- exynos_drm_fn_encoder(crtc, &mode,
- exynos_drm_encoder_crtc_dpms);
- exynos_crtc->dpms = mode;
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- exynos_drm_fn_encoder(crtc, &mode,
- exynos_drm_encoder_crtc_dpms);
- exynos_crtc->dpms = mode;
- break;
- default:
- DRM_ERROR("unspecified mode %d\n", mode);
- break;
- }
+ exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
+ exynos_crtc->dpms = mode;
mutex_unlock(&dev->struct_mutex);
}
@@ -209,30 +97,8 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
DRM_DEBUG_KMS("%s\n", __FILE__);
- /*
- * when set_crtc is requested from user or at booting time,
- * crtc->commit would be called without dpms call so if dpms is
- * no power on then crtc->dpms should be called
- * with DRM_MODE_DPMS_ON for the hardware power to be on.
- */
- if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) {
- int mode = DRM_MODE_DPMS_ON;
-
- /*
- * enable hardware(power on) to all encoders hdmi connected
- * to current crtc.
- */
- exynos_drm_crtc_dpms(crtc, mode);
- /*
- * enable dma to all encoders connected to current crtc and
- * lcd panel.
- */
- exynos_drm_fn_encoder(crtc, &mode,
- exynos_drm_encoder_dpms_from_crtc);
- }
-
- exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
- exynos_drm_encoder_crtc_commit);
+ exynos_plane_commit(exynos_crtc->plane);
+ exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
}
static bool
@@ -251,31 +117,61 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_plane *plane = exynos_crtc->plane;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+ int pipe = exynos_crtc->pipe;
+ int ret;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
+ exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
- return exynos_drm_crtc_update(crtc);
+ crtc_w = crtc->fb->width - x;
+ crtc_h = crtc->fb->height - y;
+
+ ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ x, y, crtc_w, crtc_h);
+ if (ret)
+ return ret;
+
+ plane->crtc = crtc;
+ plane->fb = crtc->fb;
+
+ exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
+
+ return 0;
}
static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_plane *plane = exynos_crtc->plane;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
- ret = exynos_drm_crtc_update(crtc);
+ crtc_w = crtc->fb->width - x;
+ crtc_h = crtc->fb->height - y;
+
+ ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
+ x, y, crtc_w, crtc_h);
if (ret)
return ret;
- exynos_drm_crtc_apply(crtc);
+ exynos_drm_crtc_commit(crtc);
- return ret;
+ return 0;
}
static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
@@ -284,6 +180,16 @@ static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
/* drm framework doesn't check NULL */
}
+static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
+{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
+ exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.dpms = exynos_drm_crtc_dpms,
.prepare = exynos_drm_crtc_prepare,
@@ -292,6 +198,7 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.mode_set = exynos_drm_crtc_mode_set,
.mode_set_base = exynos_drm_crtc_mode_set_base,
.load_lut = exynos_drm_crtc_load_lut,
+ .disable = exynos_drm_crtc_disable,
};
static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
@@ -327,7 +234,8 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
&dev_priv->pageflip_event_list);
crtc->fb = fb;
- ret = exynos_drm_crtc_update(crtc);
+ ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+ NULL);
if (ret) {
crtc->fb = old_fb;
drm_vblank_put(dev, exynos_crtc->pipe);
@@ -335,14 +243,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
goto out;
}
-
- /*
- * the values related to a buffer of the drm framebuffer
- * to be applied should be set at here. because these values
- * first, are set to shadow registers and then to
- * real registers at vsync front porch period.
- */
- exynos_drm_crtc_apply(crtc);
}
out:
mutex_unlock(&dev->struct_mutex);
@@ -362,18 +262,73 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
kfree(exynos_crtc);
}
+static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = crtc->dev;
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+ DRM_DEBUG_KMS("%s\n", __func__);
+
+ if (property == dev_priv->crtc_mode_property) {
+ enum exynos_crtc_mode mode = val;
+
+ if (mode == exynos_crtc->mode)
+ return 0;
+
+ exynos_crtc->mode = mode;
+
+ switch (mode) {
+ case CRTC_MODE_NORMAL:
+ exynos_drm_crtc_commit(crtc);
+ break;
+ case CRTC_MODE_BLANK:
+ exynos_plane_dpms(exynos_crtc->plane,
+ DRM_MODE_DPMS_OFF);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.page_flip = exynos_drm_crtc_page_flip,
.destroy = exynos_drm_crtc_destroy,
+ .set_property = exynos_drm_crtc_set_property,
+};
+
+static const struct drm_prop_enum_list mode_names[] = {
+ { CRTC_MODE_NORMAL, "normal" },
+ { CRTC_MODE_BLANK, "blank" },
};
-struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
- struct drm_crtc *crtc)
+static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
{
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+ struct drm_property *prop;
- return &exynos_crtc->overlay;
+ DRM_DEBUG_KMS("%s\n", __func__);
+
+ prop = dev_priv->crtc_mode_property;
+ if (!prop) {
+ prop = drm_property_create_enum(dev, 0, "mode", mode_names,
+ ARRAY_SIZE(mode_names));
+ if (!prop)
+ return;
+
+ dev_priv->crtc_mode_property = prop;
+ }
+
+ drm_object_attach_property(&crtc->base, prop, 0);
}
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
@@ -392,7 +347,12 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
exynos_crtc->pipe = nr;
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
- exynos_crtc->overlay.zpos = DEFAULT_ZPOS;
+ exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
+ if (!exynos_crtc->plane) {
+ kfree(exynos_crtc);
+ return -ENOMEM;
+ }
+
crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc;
@@ -400,6 +360,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
+ exynos_drm_crtc_attach_mode_property(crtc);
+
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 16b8e2195a0d..6bae8d8c250e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -29,39 +29,8 @@
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
-struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
- struct drm_crtc *crtc);
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
-/*
- * Exynos specific crtc postion structure.
- *
- * @fb_x: offset x on a framebuffer to be displyed
- * - the unit is screen coordinates.
- * @fb_y: offset y on a framebuffer to be displayed
- * - the unit is screen coordinates.
- * @src_w: width of source area to be displayed from a framebuffer.
- * @src_h: height of source area to be displayed from a framebuffer.
- * @crtc_x: offset x on hardware screen.
- * @crtc_y: offset y on hardware screen.
- * @crtc_w: width of hardware screen.
- * @crtc_h: height of hardware screen.
- */
-struct exynos_drm_crtc_pos {
- unsigned int fb_x;
- unsigned int fb_y;
- unsigned int src_w;
- unsigned int src_h;
- unsigned int crtc_x;
- unsigned int crtc_y;
- unsigned int crtc_w;
- unsigned int crtc_h;
-};
-
-int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
- struct drm_framebuffer *fb,
- struct drm_display_mode *mode,
- struct exynos_drm_crtc_pos *pos);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
index 274909271c36..613bf8a5d9b2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
@@ -25,6 +25,7 @@
#include "drmP.h"
#include "drm.h"
+#include "exynos_drm.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
@@ -86,6 +87,10 @@ static struct sg_table *
npages = buf->size / buf->page_size;
sgt = exynos_pages_to_sg(buf->pages, npages, buf->page_size);
+ if (!sgt) {
+ DRM_DEBUG_PRIME("exynos_pages_to_sg returned NULL!\n");
+ goto err_unlock;
+ }
nents = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir);
DRM_DEBUG_PRIME("npages = %d buffer size = 0x%lx page_size = 0x%lx\n",
@@ -186,7 +191,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_gem_buf *buffer;
struct page *page;
- int ret, i = 0;
+ int ret;
DRM_DEBUG_PRIME("%s\n", __FILE__);
@@ -210,7 +215,7 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
- if (IS_ERR(sgt)) {
+ if (IS_ERR_OR_NULL(sgt)) {
ret = PTR_ERR(sgt);
goto err_buf_detach;
}
@@ -236,13 +241,25 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
}
sgl = sgt->sgl;
- buffer->dma_addr = sg_dma_address(sgl);
- while (i < sgt->nents) {
- buffer->pages[i] = sg_page(sgl);
- buffer->size += sg_dma_len(sgl);
- sgl = sg_next(sgl);
- i++;
+ if (sgt->nents == 1) {
+ buffer->dma_addr = sg_dma_address(sgt->sgl);
+ buffer->size = sg_dma_len(sgt->sgl);
+
+ /* always physically continuous memory if sgt->nents is 1. */
+ exynos_gem_obj->flags |= EXYNOS_BO_CONTIG;
+ } else {
+ unsigned int i = 0;
+
+ buffer->dma_addr = sg_dma_address(sgl);
+ while (i < sgt->nents) {
+ buffer->pages[i] = sg_page(sgl);
+ buffer->size += sg_dma_len(sgl);
+ sgl = sg_next(sgl);
+ i++;
+ }
+
+ exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG;
}
exynos_gem_obj->buffer = buffer;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index d6de2e07fa03..ebacec6f1e48 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -85,8 +85,11 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
}
for (nr = 0; nr < MAX_PLANE; nr++) {
- ret = exynos_plane_init(dev, nr);
- if (ret)
+ struct drm_plane *plane;
+ unsigned int possible_crtcs = (1 << MAX_CRTC) - 1;
+
+ plane = exynos_plane_init(dev, possible_crtcs, false);
+ if (!plane)
goto err_crtc;
}
@@ -221,8 +224,6 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET,
exynos_drm_gem_get_ioctl, DRM_UNLOCKED),
- DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 277653d5fda0..e22704b249d7 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -59,12 +59,14 @@ enum exynos_drm_output_type {
*
* @mode_set: copy drm overlay info to hw specific overlay info.
* @commit: apply hardware specific overlay data to registers.
+ * @enable: enable hardware specific overlay.
* @disable: disable hardware specific overlay.
*/
struct exynos_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay);
void (*commit)(struct device *subdrv_dev, int zpos);
+ void (*enable)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev, int zpos);
};
@@ -235,6 +237,8 @@ struct exynos_drm_private {
* this array is used to be aware of which crtc did it request vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
+ struct drm_property *plane_zpos_property;
+ struct drm_property *crtc_mode_property;
};
/*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 4a13a747f5d4..2c037cd7d2d4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -30,7 +30,6 @@
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
-#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
@@ -136,21 +135,16 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
- encoder->crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
+ exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
+ if (connector->encoder == encoder)
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
-
- if (overlay_ops && overlay_ops->mode_set)
- overlay_ops->mode_set(manager->dev, overlay);
- }
}
}
@@ -310,8 +304,8 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
- if (manager->pipe == -1)
- manager->pipe = crtc;
+ if (manager->pipe != crtc)
+ return;
if (manager_ops->enable_vblank)
manager_ops->enable_vblank(manager->dev);
@@ -324,34 +318,41 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
- if (manager->pipe == -1)
- manager->pipe = crtc;
+ if (manager->pipe != crtc)
+ return;
if (manager_ops->disable_vblank)
manager_ops->disable_vblank(manager->dev);
}
-void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
- void *data)
+void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- int zpos = DEFAULT_ZPOS;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_manager *manager = exynos_encoder->manager;
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ int mode = *(int *)data;
- if (data)
- zpos = *(int *)data;
+ DRM_DEBUG_KMS("%s\n", __FILE__);
- if (overlay_ops && overlay_ops->commit)
- overlay_ops->commit(manager->dev, zpos);
+ if (manager_ops && manager_ops->dpms)
+ manager_ops->dpms(manager->dev, mode);
+
+ /*
+ * if this condition is ok then it means that the crtc is already
+ * detached from encoder and last function for detaching is properly
+ * done, so clear pipe from manager to prevent repeated call.
+ */
+ if (mode > DRM_MODE_DPMS_ON) {
+ if (!encoder->crtc)
+ manager->pipe = -1;
+ }
}
-void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
- int crtc = *(int *)data;
- int zpos = DEFAULT_ZPOS;
+ int pipe = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -359,76 +360,62 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
* when crtc is detached from encoder, this pipe is used
* to select manager operation
*/
- manager->pipe = crtc;
-
- exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
+ manager->pipe = pipe;
}
-void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- int mode = *(int *)data;
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_overlay *overlay = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_drm_encoder_dpms(encoder, mode);
-
- exynos_encoder->dpms = mode;
+ if (overlay_ops && overlay_ops->mode_set)
+ overlay_ops->mode_set(manager->dev, overlay);
}
-void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
{
- struct drm_device *dev = encoder->dev;
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_manager *manager = exynos_encoder->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
- struct drm_connector *connector;
- int mode = *(int *)data;
+ struct exynos_drm_manager *manager =
+ to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (manager_ops && manager_ops->dpms)
- manager_ops->dpms(manager->dev, mode);
-
- /*
- * set current dpms mode to the connector connected to
- * current encoder. connector->dpms would be checked
- * at drm_helper_connector_dpms()
- */
- list_for_each_entry(connector, &dev->mode_config.connector_list, head)
- if (connector->encoder == encoder)
- connector->dpms = mode;
+ if (data)
+ zpos = *(int *)data;
- /*
- * if this condition is ok then it means that the crtc is already
- * detached from encoder and last function for detaching is properly
- * done, so clear pipe from manager to prevent repeated call.
- */
- if (mode > DRM_MODE_DPMS_ON) {
- if (!encoder->crtc)
- manager->pipe = -1;
- }
+ if (overlay_ops && overlay_ops->commit)
+ overlay_ops->commit(manager->dev, zpos);
}
-void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
- struct exynos_drm_overlay *overlay = data;
+ int zpos = DEFAULT_ZPOS;
- if (overlay_ops && overlay_ops->mode_set)
- overlay_ops->mode_set(manager->dev, overlay);
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (data)
+ zpos = *(int *)data;
+
+ if (overlay_ops && overlay_ops->enable)
+ overlay_ops->enable(manager->dev, zpos);
}
-void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
+void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
- DRM_DEBUG_KMS("\n");
+ DRM_DEBUG_KMS("%s\n", __FILE__);
if (data)
zpos = *(int *)data;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index eb7d2316847e..6470d9ddf5a1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -40,13 +40,11 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *));
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
- void *data);
-void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder,
- void *data);
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 29fdbfeb43cb..a68d2b313f03 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -78,7 +78,6 @@ struct fimd_context {
struct drm_crtc *crtc;
struct clk *bus_clk;
struct clk *lcd_clk;
- struct resource *regs_res;
void __iomem *regs;
struct fimd_win_data win_data[WINDOWS_NR];
unsigned int clkdiv;
@@ -813,7 +812,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
return -EINVAL;
}
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
@@ -838,33 +837,26 @@ static int __devinit fimd_probe(struct platform_device *pdev)
goto err_clk;
}
- ctx->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!ctx->regs_res) {
- dev_err(dev, "failed to claim register region\n");
- ret = -ENOENT;
- goto err_clk;
- }
-
- ctx->regs = ioremap(res->start, resource_size(res));
+ ctx->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!ctx->regs) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
- goto err_req_region_io;
+ goto err_clk;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "irq request failed.\n");
- goto err_req_region_irq;
+ goto err_clk;
}
ctx->irq = res->start;
- ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
- if (ret < 0) {
+ ret = devm_request_irq(&pdev->dev, ctx->irq, fimd_irq_handler,
+ 0, "drm_fimd", ctx);
+ if (ret) {
dev_err(dev, "irq request failed.\n");
- goto err_req_irq;
+ goto err_clk;
}
ctx->vidcon0 = pdata->vidcon0;
@@ -899,14 +891,6 @@ static int __devinit fimd_probe(struct platform_device *pdev)
return 0;
-err_req_irq:
-err_req_region_irq:
- iounmap(ctx->regs);
-
-err_req_region_io:
- release_resource(ctx->regs_res);
- kfree(ctx->regs_res);
-
err_clk:
clk_disable(ctx->lcd_clk);
clk_put(ctx->lcd_clk);
@@ -916,7 +900,6 @@ err_bus_clk:
clk_put(ctx->bus_clk);
err_clk_get:
- kfree(ctx);
return ret;
}
@@ -944,13 +927,6 @@ out:
clk_put(ctx->lcd_clk);
clk_put(ctx->bus_clk);
- iounmap(ctx->regs);
- release_resource(ctx->regs_res);
- kfree(ctx->regs_res);
- free_irq(ctx->irq, ctx);
-
- kfree(ctx);
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 5c8b683029ea..f9efde40c097 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -99,25 +99,17 @@ out:
struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
gfp_t gfpmask)
{
- struct inode *inode;
- struct address_space *mapping;
struct page *p, **pages;
int i, npages;
- /* This is the shared memory object that backs the GEM resource */
- inode = obj->filp->f_path.dentry->d_inode;
- mapping = inode->i_mapping;
-
npages = obj->size >> PAGE_SHIFT;
pages = drm_malloc_ab(npages, sizeof(struct page *));
if (pages == NULL)
return ERR_PTR(-ENOMEM);
- gfpmask |= mapping_gfp_mask(mapping);
-
for (i = 0; i < npages; i++) {
- p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ p = alloc_page(gfpmask);
if (IS_ERR(p))
goto fail;
pages[i] = p;
@@ -126,31 +118,22 @@ struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
return pages;
fail:
- while (i--)
- page_cache_release(pages[i]);
+ while (--i)
+ __free_page(pages[i]);
drm_free_large(pages);
return ERR_PTR(PTR_ERR(p));
}
static void exynos_gem_put_pages(struct drm_gem_object *obj,
- struct page **pages,
- bool dirty, bool accessed)
+ struct page **pages)
{
- int i, npages;
+ int npages;
npages = obj->size >> PAGE_SHIFT;
- for (i = 0; i < npages; i++) {
- if (dirty)
- set_page_dirty(pages[i]);
-
- if (accessed)
- mark_page_accessed(pages[i]);
-
- /* Undo the reference we took when populating the table */
- page_cache_release(pages[i]);
- }
+ while (--npages >= 0)
+ __free_page(pages[npages]);
drm_free_large(pages);
}
@@ -189,7 +172,7 @@ static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
return -EINVAL;
}
- pages = exynos_gem_get_pages(obj, GFP_KERNEL);
+ pages = exynos_gem_get_pages(obj, GFP_HIGHUSER_MOVABLE);
if (IS_ERR(pages)) {
DRM_ERROR("failed to get pages.\n");
return PTR_ERR(pages);
@@ -230,7 +213,7 @@ err1:
kfree(buf->sgt);
buf->sgt = NULL;
err:
- exynos_gem_put_pages(obj, pages, true, false);
+ exynos_gem_put_pages(obj, pages);
return ret;
}
@@ -248,7 +231,7 @@ static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
kfree(buf->sgt);
buf->sgt = NULL;
- exynos_gem_put_pages(obj, buf->pages, true, false);
+ exynos_gem_put_pages(obj, buf->pages);
buf->pages = NULL;
/* add some codes for UNCACHED type here. TODO */
@@ -291,11 +274,21 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
if (!buf->pages)
return;
+ /*
+ * do not release memory region from exporter.
+ *
+ * the region will be released by exporter
+ * once dmabuf's refcount becomes 0.
+ */
+ if (obj->import_attach)
+ goto out;
+
if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG)
exynos_drm_gem_put_pages(obj);
else
exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
+out:
exynos_drm_fini_buf(obj->dev, buf);
exynos_gem_obj->buffer = NULL;
@@ -668,7 +661,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
* with DRM_IOCTL_MODE_CREATE_DUMB command.
*/
- args->pitch = args->width * args->bpp >> 3;
+ args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = PAGE_ALIGN(args->pitch * args->height);
exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 14d038b6cb02..085b2a5d5f70 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -63,7 +63,8 @@ struct exynos_drm_gem_buf {
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
- * @size: total memory size to physically non-continuous memory region.
+ * @size: size requested from user, in bytes and this size is aligned
+ * in page unit.
* @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transfered to user as kms_bo.handle so
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index c4c6525d4653..b89829e5043a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -12,9 +12,12 @@
#include "drmP.h"
#include "exynos_drm.h"
-#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
+#include "exynos_drm_fb.h"
+#include "exynos_drm_gem.h"
+
+#define to_exynos_plane(x) container_of(x, struct exynos_plane, base)
struct exynos_plane {
struct drm_plane base;
@@ -30,6 +33,108 @@ static const uint32_t formats[] = {
DRM_FORMAT_NV12MT,
};
+int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+ struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+ unsigned int actual_w;
+ unsigned int actual_h;
+ int nr;
+ int i;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ nr = exynos_drm_format_num_buffers(fb->pixel_format);
+ for (i = 0; i < nr; i++) {
+ struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
+
+ if (!buffer) {
+ DRM_LOG_KMS("buffer is null\n");
+ return -EFAULT;
+ }
+
+ overlay->dma_addr[i] = buffer->dma_addr;
+ overlay->vaddr[i] = buffer->kvaddr;
+
+ DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
+ i, (unsigned long)overlay->vaddr[i],
+ (unsigned long)overlay->dma_addr[i]);
+ }
+
+ actual_w = min((unsigned)(crtc->mode.hdisplay - crtc_x), crtc_w);
+ actual_h = min((unsigned)(crtc->mode.vdisplay - crtc_y), crtc_h);
+
+ /* set drm framebuffer data. */
+ overlay->fb_x = src_x;
+ overlay->fb_y = src_y;
+ overlay->fb_width = fb->width;
+ overlay->fb_height = fb->height;
+ overlay->src_width = src_w;
+ overlay->src_height = src_h;
+ overlay->bpp = fb->bits_per_pixel;
+ overlay->pitch = fb->pitches[0];
+ overlay->pixel_format = fb->pixel_format;
+
+ /* set overlay range to be displayed. */
+ overlay->crtc_x = crtc_x;
+ overlay->crtc_y = crtc_y;
+ overlay->crtc_width = actual_w;
+ overlay->crtc_height = actual_h;
+
+ /* set drm mode data. */
+ overlay->mode_width = crtc->mode.hdisplay;
+ overlay->mode_height = crtc->mode.vdisplay;
+ overlay->refresh = crtc->mode.vrefresh;
+ overlay->scan_flag = crtc->mode.flags;
+
+ DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
+ overlay->crtc_x, overlay->crtc_y,
+ overlay->crtc_width, overlay->crtc_height);
+
+ exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
+
+ return 0;
+}
+
+void exynos_plane_commit(struct drm_plane *plane)
+{
+ struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+ struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+
+ exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ exynos_drm_encoder_plane_commit);
+}
+
+void exynos_plane_dpms(struct drm_plane *plane, int mode)
+{
+ struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+ struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ if (exynos_plane->enabled)
+ return;
+
+ exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ exynos_drm_encoder_plane_enable);
+
+ exynos_plane->enabled = true;
+ } else {
+ if (!exynos_plane->enabled)
+ return;
+
+ exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+ exynos_drm_encoder_plane_disable);
+
+ exynos_plane->enabled = false;
+ }
+}
+
static int
exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -37,64 +142,37 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
- struct exynos_plane *exynos_plane =
- container_of(plane, struct exynos_plane, base);
- struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
- struct exynos_drm_crtc_pos pos;
int ret;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
- pos.crtc_x = crtc_x;
- pos.crtc_y = crtc_y;
- pos.crtc_w = crtc_w;
- pos.crtc_h = crtc_h;
-
- /* considering 16.16 fixed point of source values */
- pos.fb_x = src_x >> 16;
- pos.fb_y = src_y >> 16;
- pos.src_w = src_w >> 16;
- pos.src_h = src_h >> 16;
-
- ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos);
+ ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
+ crtc_w, crtc_h, src_x >> 16, src_y >> 16,
+ src_w >> 16, src_h >> 16);
if (ret < 0)
return ret;
- exynos_drm_fn_encoder(crtc, overlay,
- exynos_drm_encoder_crtc_mode_set);
- exynos_drm_fn_encoder(crtc, &overlay->zpos,
- exynos_drm_encoder_crtc_plane_commit);
+ plane->crtc = crtc;
+ plane->fb = crtc->fb;
- exynos_plane->enabled = true;
+ exynos_plane_commit(plane);
+ exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
return 0;
}
static int exynos_disable_plane(struct drm_plane *plane)
{
- struct exynos_plane *exynos_plane =
- container_of(plane, struct exynos_plane, base);
- struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
-
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- if (!exynos_plane->enabled)
- return 0;
-
- exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
- exynos_drm_encoder_crtc_disable);
-
- exynos_plane->enabled = false;
- exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+ exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF);
return 0;
}
static void exynos_plane_destroy(struct drm_plane *plane)
{
- struct exynos_plane *exynos_plane =
- container_of(plane, struct exynos_plane, base);
+ struct exynos_plane *exynos_plane = to_exynos_plane(plane);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -103,69 +181,79 @@ static void exynos_plane_destroy(struct drm_plane *plane)
kfree(exynos_plane);
}
+static int exynos_plane_set_property(struct drm_plane *plane,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct exynos_plane *exynos_plane = to_exynos_plane(plane);
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (property == dev_priv->plane_zpos_property) {
+ exynos_plane->overlay.zpos = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static struct drm_plane_funcs exynos_plane_funcs = {
.update_plane = exynos_update_plane,
.disable_plane = exynos_disable_plane,
.destroy = exynos_plane_destroy,
+ .set_property = exynos_plane_set_property,
};
-int exynos_plane_init(struct drm_device *dev, unsigned int nr)
+static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
{
- struct exynos_plane *exynos_plane;
- uint32_t possible_crtcs;
+ struct drm_device *dev = plane->dev;
+ struct exynos_drm_private *dev_priv = dev->dev_private;
+ struct drm_property *prop;
- exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
- if (!exynos_plane)
- return -ENOMEM;
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- /* all CRTCs are available */
- possible_crtcs = (1 << MAX_CRTC) - 1;
+ prop = dev_priv->plane_zpos_property;
+ if (!prop) {
+ prop = drm_property_create_range(dev, 0, "zpos", 0,
+ MAX_PLANE - 1);
+ if (!prop)
+ return;
- exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+ dev_priv->plane_zpos_property = prop;
+ }
- return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
- &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
- false);
+ drm_object_attach_property(&plane->base, prop, 0);
}
-int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+struct drm_plane *exynos_plane_init(struct drm_device *dev,
+ unsigned int possible_crtcs, bool priv)
{
- struct drm_exynos_plane_set_zpos *zpos_req = data;
- struct drm_mode_object *obj;
- struct drm_plane *plane;
struct exynos_plane *exynos_plane;
- int ret = 0;
+ int err;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
-
- if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) {
- if (zpos_req->zpos != DEFAULT_ZPOS) {
- DRM_ERROR("zpos not within limits\n");
- return -EINVAL;
- }
+ exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
+ if (!exynos_plane) {
+ DRM_ERROR("failed to allocate plane\n");
+ return NULL;
}
- mutex_lock(&dev->mode_config.mutex);
-
- obj = drm_mode_object_find(dev, zpos_req->plane_id,
- DRM_MODE_OBJECT_PLANE);
- if (!obj) {
- DRM_DEBUG_KMS("Unknown plane ID %d\n",
- zpos_req->plane_id);
- ret = -EINVAL;
- goto out;
+ err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
+ &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
+ priv);
+ if (err) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(exynos_plane);
+ return NULL;
}
- plane = obj_to_plane(obj);
- exynos_plane = container_of(plane, struct exynos_plane, base);
-
- exynos_plane->overlay.zpos = zpos_req->zpos;
+ if (priv)
+ exynos_plane->overlay.zpos = DEFAULT_ZPOS;
+ else
+ exynos_plane_attach_zpos_property(&exynos_plane->base);
-out:
- mutex_unlock(&dev->mode_config.mutex);
- return ret;
+ return &exynos_plane->base;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h
index 16b71f8217e7..88312458580d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h
@@ -9,6 +9,12 @@
*
*/
-int exynos_plane_init(struct drm_device *dev, unsigned int nr);
-int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h);
+void exynos_plane_commit(struct drm_plane *plane);
+void exynos_plane_dpms(struct drm_plane *plane, int mode);
+struct drm_plane *exynos_plane_init(struct drm_device *dev,
+ unsigned int possible_crtcs, bool priv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 7b9c153dceb6..bb1550c4dd57 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -85,8 +85,6 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06
};
-static void vidi_fake_vblank_handler(struct work_struct *work);
-
static bool vidi_display_is_connected(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
@@ -531,6 +529,16 @@ static int vidi_store_connection(struct device *dev,
if (ctx->connected > 1)
return -EINVAL;
+ /* use fake edid data for test. */
+ if (!ctx->raw_edid)
+ ctx->raw_edid = (struct edid *)fake_edid_info;
+
+ /* if raw_edid isn't same as fake data then it can't be tested. */
+ if (ctx->raw_edid != (struct edid *)fake_edid_info) {
+ DRM_DEBUG_KMS("edid data is not fake data.\n");
+ return -EINVAL;
+ }
+
DRM_DEBUG_KMS("requested connection.\n");
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
@@ -549,6 +557,8 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
struct exynos_drm_manager *manager;
struct exynos_drm_display_ops *display_ops;
struct drm_exynos_vidi_connection *vidi = data;
+ struct edid *raw_edid;
+ int edid_len;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -557,11 +567,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
return -EINVAL;
}
- if (!vidi->edid) {
- DRM_DEBUG_KMS("edid data is null.\n");
- return -EINVAL;
- }
-
if (vidi->connection > 1) {
DRM_DEBUG_KMS("connection should be 0 or 1.\n");
return -EINVAL;
@@ -588,8 +593,30 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
return -EINVAL;
}
- if (vidi->connection)
- ctx->raw_edid = (struct edid *)vidi->edid;
+ if (vidi->connection) {
+ if (!vidi->edid) {
+ DRM_DEBUG_KMS("edid data is null.\n");
+ return -EINVAL;
+ }
+ raw_edid = (struct edid *)(uint32_t)vidi->edid;
+ edid_len = (1 + raw_edid->extensions) * EDID_LENGTH;
+ ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL);
+ if (!ctx->raw_edid) {
+ DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
+ return -ENOMEM;
+ }
+ memcpy(ctx->raw_edid, raw_edid, edid_len);
+ } else {
+ /*
+ * with connection = 0, free raw_edid
+ * only if raw edid data isn't same as fake data.
+ */
+ if (ctx->raw_edid && ctx->raw_edid !=
+ (struct edid *)fake_edid_info) {
+ kfree(ctx->raw_edid);
+ ctx->raw_edid = NULL;
+ }
+ }
ctx->connected = vidi->connection;
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
@@ -614,9 +641,6 @@ static int __devinit vidi_probe(struct platform_device *pdev)
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
- /* for test */
- ctx->raw_edid = (struct edid *)fake_edid_info;
-
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &vidi_manager;
@@ -644,6 +668,11 @@ static int __devexit vidi_remove(struct platform_device *pdev)
exynos_drm_subdrv_unregister(&ctx->subdrv);
+ if (ctx->raw_edid != (struct edid *)fake_edid_info) {
+ kfree(ctx->raw_edid);
+ ctx->raw_edid = NULL;
+ }
+
kfree(ctx);
return 0;
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 066bde3f19c4..409e2ec1207c 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -63,7 +63,6 @@ struct hdmi_context {
bool dvi_mode;
struct mutex hdmi_mutex;
- struct resource *regs_res;
void __iomem *regs;
unsigned int external_irq;
unsigned int internal_irq;
@@ -2280,16 +2279,17 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
return -EINVAL;
}
- drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
+ drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
+ GFP_KERNEL);
if (!drm_hdmi_ctx) {
DRM_ERROR("failed to allocate common hdmi context.\n");
return -ENOMEM;
}
- hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
+ hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context),
+ GFP_KERNEL);
if (!hdata) {
DRM_ERROR("out of memory\n");
- kfree(drm_hdmi_ctx);
return -ENOMEM;
}
@@ -2318,26 +2318,18 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
goto err_resource;
}
- hdata->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!hdata->regs_res) {
- DRM_ERROR("failed to claim register region\n");
- ret = -ENOENT;
- goto err_resource;
- }
-
- hdata->regs = ioremap(res->start, resource_size(res));
+ hdata->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hdata->regs) {
DRM_ERROR("failed to map registers\n");
ret = -ENXIO;
- goto err_req_region;
+ goto err_resource;
}
/* DDC i2c driver */
if (i2c_add_driver(&ddc_driver)) {
DRM_ERROR("failed to register ddc i2c driver\n");
ret = -ENOENT;
- goto err_iomap;
+ goto err_resource;
}
hdata->ddc_port = hdmi_ddc;
@@ -2398,16 +2390,9 @@ err_hdmiphy:
i2c_del_driver(&hdmiphy_driver);
err_ddc:
i2c_del_driver(&ddc_driver);
-err_iomap:
- iounmap(hdata->regs);
-err_req_region:
- release_mem_region(hdata->regs_res->start,
- resource_size(hdata->regs_res));
err_resource:
hdmi_resources_cleanup(hdata);
err_data:
- kfree(hdata);
- kfree(drm_hdmi_ctx);
return ret;
}
@@ -2425,18 +2410,11 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
hdmi_resources_cleanup(hdata);
- iounmap(hdata->regs);
-
- release_mem_region(hdata->regs_res->start,
- resource_size(hdata->regs_res));
-
/* hdmiphy i2c driver */
i2c_del_driver(&hdmiphy_driver);
/* DDC i2c driver */
i2c_del_driver(&ddc_driver);
- kfree(hdata);
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index e2147a2ddcec..30fcc12f81dd 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -956,7 +956,8 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
- mixer_res->mixer_regs = ioremap(res->start, resource_size(res));
+ mixer_res->mixer_regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (mixer_res->mixer_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
@@ -967,38 +968,34 @@ static int __devinit mixer_resources_init(struct exynos_drm_hdmi_context *ctx,
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
ret = -ENXIO;
- goto fail_mixer_regs;
+ goto fail;
}
- mixer_res->vp_regs = ioremap(res->start, resource_size(res));
+ mixer_res->vp_regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (mixer_res->vp_regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
- goto fail_mixer_regs;
+ goto fail;
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
ret = -ENXIO;
- goto fail_vp_regs;
+ goto fail;
}
- ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx);
+ ret = devm_request_irq(&pdev->dev, res->start, mixer_irq_handler,
+ 0, "drm_mixer", ctx);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
- goto fail_vp_regs;
+ goto fail;
}
mixer_res->irq = res->start;
return 0;
-fail_vp_regs:
- iounmap(mixer_res->vp_regs);
-
-fail_mixer_regs:
- iounmap(mixer_res->mixer_regs);
-
fail:
if (!IS_ERR_OR_NULL(mixer_res->sclk_dac))
clk_put(mixer_res->sclk_dac);
@@ -1013,16 +1010,6 @@ fail:
return ret;
}
-static void mixer_resources_cleanup(struct mixer_context *ctx)
-{
- struct mixer_resources *res = &ctx->mixer_res;
-
- free_irq(res->irq, ctx);
-
- iounmap(res->vp_regs);
- iounmap(res->mixer_regs);
-}
-
static int __devinit mixer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1032,16 +1019,16 @@ static int __devinit mixer_probe(struct platform_device *pdev)
dev_info(dev, "probe start\n");
- drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
+ drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
+ GFP_KERNEL);
if (!drm_hdmi_ctx) {
DRM_ERROR("failed to allocate common hdmi context.\n");
return -ENOMEM;
}
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_ERROR("failed to alloc mixer context.\n");
- kfree(drm_hdmi_ctx);
return -ENOMEM;
}
@@ -1072,17 +1059,10 @@ fail:
static int mixer_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx =
- platform_get_drvdata(pdev);
- struct mixer_context *ctx = drm_hdmi_ctx->ctx;
-
- dev_info(dev, "remove successful\n");
+ dev_info(&pdev->dev, "remove successful\n");
pm_runtime_disable(&pdev->dev);
- mixer_resources_cleanup(ctx);
-
return 0;
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 500844f04f93..60ea284407ce 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1928,6 +1928,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_RADIOSHARK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 41c34f21bd00..1dcb76ff51e3 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -333,6 +333,7 @@
#define USB_VENDOR_ID_GRIFFIN 0x077d
#define USB_DEVICE_ID_POWERMATE 0x0410
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
+#define USB_DEVICE_ID_RADIOSHARK 0x627a
#define USB_VENDOR_ID_GTCO 0x078c
#define USB_DEVICE_ID_GTCO_90 0x0090
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index a220e5746d67..4748086eaaf2 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -545,8 +545,7 @@ static int vmbus_bus_init(int irq)
if (ret)
goto err_cleanup;
- ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
- driver_name, hv_acpi_dev);
+ ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev);
if (ret != 0) {
pr_err("Unable to request IRQ %d\n",
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 563c02904ddf..23ab3c496b05 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -927,6 +927,8 @@ static int acpi_power_meter_remove(struct acpi_device *device, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+
static int acpi_power_meter_resume(struct device *dev)
{
struct acpi_power_meter_resource *resource;
@@ -944,6 +946,8 @@ static int acpi_power_meter_resume(struct device *dev)
return 0;
}
+#endif /* CONFIG_PM_SLEEP */
+
static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume);
static struct acpi_driver acpi_power_meter_driver = {
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4d937a18fadb..282708860517 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -55,9 +55,9 @@
/* wait up to 32 ms for a status change. */
#define APPLESMC_MIN_WAIT 0x0010
+#define APPLESMC_RETRY_WAIT 0x0100
#define APPLESMC_MAX_WAIT 0x8000
-#define APPLESMC_STATUS_MASK 0x0f
#define APPLESMC_READ_CMD 0x10
#define APPLESMC_WRITE_CMD 0x11
#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
@@ -162,51 +162,68 @@ static unsigned int key_at_index;
static struct workqueue_struct *applesmc_led_wq;
/*
- * __wait_status - Wait up to 32ms for the status port to get a certain value
- * (masked with 0x0f), returning zero if the value is obtained. Callers must
+ * wait_read - Wait for a byte to appear on SMC port. Callers must
* hold applesmc_lock.
*/
-static int __wait_status(u8 val)
+static int wait_read(void)
{
+ u8 status;
int us;
-
- val = val & APPLESMC_STATUS_MASK;
-
for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
udelay(us);
- if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
+ status = inb(APPLESMC_CMD_PORT);
+ /* read: wait for smc to settle */
+ if (status & 0x01)
return 0;
}
+ pr_warn("wait_read() fail: 0x%02x\n", status);
return -EIO;
}
/*
- * special treatment of command port - on newer macbooks, it seems necessary
- * to resend the command byte before polling the status again. Callers must
- * hold applesmc_lock.
+ * send_byte - Write to SMC port, retrying when necessary. Callers
+ * must hold applesmc_lock.
*/
-static int send_command(u8 cmd)
+static int send_byte(u8 cmd, u16 port)
{
+ u8 status;
int us;
+
+ outb(cmd, port);
for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
- outb(cmd, APPLESMC_CMD_PORT);
udelay(us);
- if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
+ status = inb(APPLESMC_CMD_PORT);
+ /* write: wait for smc to settle */
+ if (status & 0x02)
+ continue;
+ /* ready: cmd accepted, return */
+ if (status & 0x04)
return 0;
+ /* timeout: give up */
+ if (us << 1 == APPLESMC_MAX_WAIT)
+ break;
+ /* busy: long wait and resend */
+ udelay(APPLESMC_RETRY_WAIT);
+ outb(cmd, port);
}
+
+ pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status);
return -EIO;
}
+static int send_command(u8 cmd)
+{
+ return send_byte(cmd, APPLESMC_CMD_PORT);
+}
+
static int send_argument(const char *key)
{
int i;
- for (i = 0; i < 4; i++) {
- outb(key[i], APPLESMC_DATA_PORT);
- if (__wait_status(0x04))
+ for (i = 0; i < 4; i++)
+ if (send_byte(key[i], APPLESMC_DATA_PORT))
return -EIO;
- }
return 0;
}
@@ -219,11 +236,14 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
return -EIO;
}
- outb(len, APPLESMC_DATA_PORT);
+ if (send_byte(len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: read len fail\n", key);
+ return -EIO;
+ }
for (i = 0; i < len; i++) {
- if (__wait_status(0x05)) {
- pr_warn("%.4s: read data fail\n", key);
+ if (wait_read()) {
+ pr_warn("%.4s: read data[%d] fail\n", key, i);
return -EIO;
}
buffer[i] = inb(APPLESMC_DATA_PORT);
@@ -241,14 +261,16 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
return -EIO;
}
- outb(len, APPLESMC_DATA_PORT);
+ if (send_byte(len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: write len fail\n", key);
+ return -EIO;
+ }
for (i = 0; i < len; i++) {
- if (__wait_status(0x04)) {
+ if (send_byte(buffer[i], APPLESMC_DATA_PORT)) {
pr_warn("%s: write data fail\n", key);
return -EIO;
}
- outb(buffer[i], APPLESMC_DATA_PORT);
}
return 0;
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 637c51c11b44..faa16f80db9c 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -793,7 +793,7 @@ static struct notifier_block coretemp_cpu_notifier __refdata = {
.notifier_call = coretemp_cpu_callback,
};
-static const struct x86_cpu_id coretemp_ids[] = {
+static const struct x86_cpu_id __initconst coretemp_ids[] = {
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM },
{}
};
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index e72ba5d2a824..e21e43c13156 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -57,7 +57,7 @@ static const unsigned short normal_i2c[] = {
#define JC42_CFG_EVENT_LOCK (1 << 7)
#define JC42_CFG_SHUTDOWN (1 << 8)
#define JC42_CFG_HYST_SHIFT 9
-#define JC42_CFG_HYST_MASK 0x03
+#define JC42_CFG_HYST_MASK (0x03 << 9)
/* Capabilities */
#define JC42_CAP_RANGE (1 << 2)
@@ -287,8 +287,8 @@ static ssize_t show_temp_crit_hyst(struct device *dev,
return PTR_ERR(data);
temp = jc42_temp_from_reg(data->temp_crit);
- hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT)
- & JC42_CFG_HYST_MASK];
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
return sprintf(buf, "%d\n", temp - hyst);
}
@@ -302,8 +302,8 @@ static ssize_t show_temp_max_hyst(struct device *dev,
return PTR_ERR(data);
temp = jc42_temp_from_reg(data->temp_max);
- hyst = jc42_hysteresis[(data->config >> JC42_CFG_HYST_SHIFT)
- & JC42_CFG_HYST_MASK];
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
return sprintf(buf, "%d\n", temp - hyst);
}
@@ -362,8 +362,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
}
mutex_lock(&data->update_lock);
- data->config = (data->config
- & ~(JC42_CFG_HYST_MASK << JC42_CFG_HYST_SHIFT))
+ data->config = (data->config & ~JC42_CFG_HYST_MASK)
| (hyst << JC42_CFG_HYST_SHIFT);
err = i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
data->config);
@@ -535,9 +534,16 @@ static int jc42_remove(struct i2c_client *client)
struct jc42_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &jc42_group);
- if (data->config != data->orig_config)
- i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG,
- data->orig_config);
+
+ /* Restore original configuration except hysteresis */
+ if ((data->config & ~JC42_CFG_HYST_MASK) !=
+ (data->orig_config & ~JC42_CFG_HYST_MASK)) {
+ int config;
+
+ config = (data->orig_config & ~JC42_CFG_HYST_MASK)
+ | (data->config & JC42_CFG_HYST_MASK);
+ i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config);
+ }
return 0;
}
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 8689664ef03c..ee4ebc198a94 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -309,7 +309,7 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = {
.notifier_call = via_cputemp_cpu_callback,
};
-static const struct x86_cpu_id cputemp_ids[] = {
+static const struct x86_cpu_id __initconst cputemp_ids[] = {
{ X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
{ X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
{ X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2e7530a4e7b8..b4aaa1bd6728 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -462,7 +462,7 @@ config I2C_MPC
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
- depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
+ depends on (MV64X60 || PLAT_ORION)
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -483,10 +483,11 @@ config I2C_MXS
config I2C_NOMADIK
tristate "ST-Ericsson Nomadik/Ux500 I2C Controller"
- depends on PLAT_NOMADIK
+ depends on ARM_AMBA
help
If you say yes to this option, support will be included for the
- I2C interface from ST-Ericsson's Nomadik and Ux500 architectures.
+ I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
+ as well as the STA2X11 PCIe I/O HUB.
config I2C_NUC900
tristate "NUC900 I2C Driver"
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 1679deef9c89..e24484beef07 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -279,30 +279,31 @@ static int __devexit at91_i2c_remove(struct platform_device *pdev)
/* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */
-static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int at91_i2c_suspend(struct device *dev)
{
clk_disable(twi_clk);
return 0;
}
-static int at91_i2c_resume(struct platform_device *pdev)
+static int at91_i2c_resume(struct device *dev)
{
return clk_enable(twi_clk);
}
+static SIMPLE_DEV_PM_OPS(at91_i2c_pm, at91_i2c_suspend, at91_i2c_resume);
+#define AT91_I2C_PM (&at91_i2c_pm)
+
#else
-#define at91_i2c_suspend NULL
-#define at91_i2c_resume NULL
+#define AT91_I2C_PM NULL
#endif
static struct platform_driver at91_i2c_driver = {
.probe = at91_i2c_probe,
.remove = __devexit_p(at91_i2c_remove),
- .suspend = at91_i2c_suspend,
- .resume = at91_i2c_resume,
.driver = {
.name = "at91_i2c",
.owner = THIS_MODULE,
+ .pm = AT91_I2C_PM,
},
};
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index cdb59e5b23f7..0cf780fd6ef1 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -25,6 +25,7 @@
#include <asm/blackfin.h>
#include <asm/portmux.h>
#include <asm/irq.h>
+#include <asm/bfin_twi.h>
/* SMBus mode*/
#define TWI_I2C_MODE_STANDARD 1
@@ -32,56 +33,6 @@
#define TWI_I2C_MODE_COMBINED 3
#define TWI_I2C_MODE_REPEAT 4
-struct bfin_twi_iface {
- int irq;
- spinlock_t lock;
- char read_write;
- u8 command;
- u8 *transPtr;
- int readNum;
- int writeNum;
- int cur_mode;
- int manual_stop;
- int result;
- struct i2c_adapter adap;
- struct completion complete;
- struct i2c_msg *pmsg;
- int msg_num;
- int cur_msg;
- u16 saved_clkdiv;
- u16 saved_control;
- void __iomem *regs_base;
-};
-
-
-#define DEFINE_TWI_REG(reg, off) \
-static inline u16 read_##reg(struct bfin_twi_iface *iface) \
- { return bfin_read16(iface->regs_base + (off)); } \
-static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \
- { bfin_write16(iface->regs_base + (off), v); }
-
-DEFINE_TWI_REG(CLKDIV, 0x00)
-DEFINE_TWI_REG(CONTROL, 0x04)
-DEFINE_TWI_REG(SLAVE_CTL, 0x08)
-DEFINE_TWI_REG(SLAVE_STAT, 0x0C)
-DEFINE_TWI_REG(SLAVE_ADDR, 0x10)
-DEFINE_TWI_REG(MASTER_CTL, 0x14)
-DEFINE_TWI_REG(MASTER_STAT, 0x18)
-DEFINE_TWI_REG(MASTER_ADDR, 0x1C)
-DEFINE_TWI_REG(INT_STAT, 0x20)
-DEFINE_TWI_REG(INT_MASK, 0x24)
-DEFINE_TWI_REG(FIFO_CTL, 0x28)
-DEFINE_TWI_REG(FIFO_STAT, 0x2C)
-DEFINE_TWI_REG(XMT_DATA8, 0x80)
-DEFINE_TWI_REG(XMT_DATA16, 0x84)
-DEFINE_TWI_REG(RCV_DATA8, 0x88)
-DEFINE_TWI_REG(RCV_DATA16, 0x8C)
-
-static const u16 pin_req[2][3] = {
- {P_TWI0_SCL, P_TWI0_SDA, 0},
- {P_TWI1_SCL, P_TWI1_SDA, 0},
-};
-
static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
unsigned short twi_int_status)
{
@@ -99,7 +50,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
*/
else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | MDIR | RSTART);
+ read_MASTER_CTL(iface) | MDIR);
else if (iface->manual_stop)
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP);
@@ -107,10 +58,10 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
iface->cur_msg + 1 < iface->msg_num) {
if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | RSTART | MDIR);
+ read_MASTER_CTL(iface) | MDIR);
else
write_MASTER_CTL(iface,
- (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+ read_MASTER_CTL(iface) & ~MDIR);
}
}
if (twi_int_status & RCVSERV) {
@@ -130,17 +81,25 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
}
iface->transPtr++;
iface->readNum--;
- } else if (iface->manual_stop) {
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | STOP);
- } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg + 1 < iface->msg_num) {
- if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | RSTART | MDIR);
- else
+ }
+
+ if (iface->readNum == 0) {
+ if (iface->manual_stop) {
+ /* Temporary workaround to avoid possible bus stall -
+ * Flush FIFO before issuing the STOP condition
+ */
+ read_RCV_DATA16(iface);
write_MASTER_CTL(iface,
- (read_MASTER_CTL(iface) | RSTART) & ~MDIR);
+ read_MASTER_CTL(iface) | STOP);
+ } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg + 1 < iface->msg_num) {
+ if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) | MDIR);
+ else
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) & ~MDIR);
+ }
}
}
if (twi_int_status & MERR) {
@@ -193,7 +152,8 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
return;
}
if (twi_int_status & MCOMP) {
- if ((read_MASTER_CTL(iface) & MEN) == 0 &&
+ if (twi_int_status & (XMTSERV | RCVSERV) &&
+ (read_MASTER_CTL(iface) & MEN) == 0 &&
(iface->cur_mode == TWI_I2C_MODE_REPEAT ||
iface->cur_mode == TWI_I2C_MODE_COMBINED)) {
iface->result = -1;
@@ -221,7 +181,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
write_MASTER_CTL(iface,
read_MASTER_CTL(iface) & ~RSTART);
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg+1 < iface->msg_num) {
+ iface->cur_msg + 1 < iface->msg_num) {
iface->cur_msg++;
iface->transPtr = iface->pmsg[iface->cur_msg].buf;
iface->writeNum = iface->readNum =
@@ -241,27 +201,29 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
}
}
- if (iface->pmsg[iface->cur_msg].len <= 255)
- write_MASTER_CTL(iface,
+ if (iface->pmsg[iface->cur_msg].len <= 255) {
+ write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) &
(~(0xff << 6))) |
- (iface->pmsg[iface->cur_msg].len << 6));
- else {
+ (iface->pmsg[iface->cur_msg].len << 6));
+ iface->manual_stop = 0;
+ } else {
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) |
(0xff << 6)));
iface->manual_stop = 1;
}
- /* remove restart bit and enable master receive */
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) & ~RSTART);
+ /* remove restart bit before last message */
+ if (iface->cur_msg + 1 == iface->msg_num)
+ write_MASTER_CTL(iface,
+ read_MASTER_CTL(iface) & ~RSTART);
} else {
iface->result = 1;
write_INT_MASK(iface, 0);
write_MASTER_CTL(iface, 0);
}
+ complete(&iface->complete);
}
- complete(&iface->complete);
}
/* Interrupt handler */
@@ -298,8 +260,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
if (!(read_CONTROL(iface) & TWI_ENA))
return -ENXIO;
- while (read_MASTER_STAT(iface) & BUSBUSY)
- yield();
+ if (read_MASTER_STAT(iface) & BUSBUSY)
+ return -EAGAIN;
iface->pmsg = msgs;
iface->msg_num = num;
@@ -311,7 +273,8 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
return -EINVAL;
}
- iface->cur_mode = TWI_I2C_MODE_REPEAT;
+ if (iface->msg_num > 1)
+ iface->cur_mode = TWI_I2C_MODE_REPEAT;
iface->manual_stop = 0;
iface->transPtr = pmsg->buf;
iface->writeNum = iface->readNum = pmsg->len;
@@ -356,6 +319,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
/* Master enable */
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
+ (iface->msg_num > 1 ? RSTART : 0) |
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
SSYNC();
@@ -398,8 +362,8 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
if (!(read_CONTROL(iface) & TWI_ENA))
return -ENXIO;
- while (read_MASTER_STAT(iface) & BUSBUSY)
- yield();
+ if (read_MASTER_STAT(iface) & BUSBUSY)
+ return -EAGAIN;
iface->writeNum = 0;
iface->readNum = 0;
@@ -520,7 +484,7 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
else
write_MASTER_CTL(iface, 0x1 << 6);
/* Master enable */
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
+ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | RSTART |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
break;
default:
@@ -611,9 +575,9 @@ static struct i2c_algorithm bfin_twi_algorithm = {
.functionality = bfin_twi_functionality,
};
-static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state)
+static int i2c_bfin_twi_suspend(struct device *dev)
{
- struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+ struct bfin_twi_iface *iface = dev_get_drvdata(dev);
iface->saved_clkdiv = read_CLKDIV(iface);
iface->saved_control = read_CONTROL(iface);
@@ -626,14 +590,14 @@ static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state
return 0;
}
-static int i2c_bfin_twi_resume(struct platform_device *pdev)
+static int i2c_bfin_twi_resume(struct device *dev)
{
- struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
+ struct bfin_twi_iface *iface = dev_get_drvdata(dev);
int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- 0, pdev->name, iface);
+ 0, to_platform_device(dev)->name, iface);
if (rc) {
- dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
+ dev_err(dev, "Can't get IRQ %d !\n", iface->irq);
return -ENODEV;
}
@@ -646,6 +610,9 @@ static int i2c_bfin_twi_resume(struct platform_device *pdev)
return 0;
}
+static SIMPLE_DEV_PM_OPS(i2c_bfin_twi_pm,
+ i2c_bfin_twi_suspend, i2c_bfin_twi_resume);
+
static int i2c_bfin_twi_probe(struct platform_device *pdev)
{
struct bfin_twi_iface *iface;
@@ -695,7 +662,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
p_adap->timeout = 5 * HZ;
p_adap->retries = 3;
- rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
+ rc = peripheral_request_list((unsigned short *)pdev->dev.platform_data,
+ "i2c-bfin-twi");
if (rc) {
dev_err(&pdev->dev, "Can't setup pin mux!\n");
goto out_error_pin_mux;
@@ -742,7 +710,7 @@ out_error_add_adapter:
free_irq(iface->irq, iface);
out_error_req_irq:
out_error_no_irq:
- peripheral_free_list(pin_req[pdev->id]);
+ peripheral_free_list((unsigned short *)pdev->dev.platform_data);
out_error_pin_mux:
iounmap(iface->regs_base);
out_error_ioremap:
@@ -760,7 +728,7 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev)
i2c_del_adapter(&(iface->adap));
free_irq(iface->irq, iface);
- peripheral_free_list(pin_req[pdev->id]);
+ peripheral_free_list((unsigned short *)pdev->dev.platform_data);
iounmap(iface->regs_base);
kfree(iface);
@@ -770,11 +738,10 @@ static int i2c_bfin_twi_remove(struct platform_device *pdev)
static struct platform_driver i2c_bfin_twi_driver = {
.probe = i2c_bfin_twi_probe,
.remove = i2c_bfin_twi_remove,
- .suspend = i2c_bfin_twi_suspend,
- .resume = i2c_bfin_twi_resume,
.driver = {
.name = "i2c-bfin-twi",
.owner = THIS_MODULE,
+ .pm = &i2c_bfin_twi_pm,
},
};
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 370031ac8200..0722f869465c 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -117,10 +117,8 @@ static u16 __initdata i2c_clk_div[50][2] = {
struct imx_i2c_struct {
struct i2c_adapter adapter;
- struct resource *res;
struct clk *clk;
void __iomem *base;
- int irq;
wait_queue_head_t queue;
unsigned long i2csr;
unsigned int disable_delay;
@@ -472,9 +470,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
struct pinctrl *pinctrl;
void __iomem *base;
- resource_size_t res_size;
- int irq, bitrate;
- int ret;
+ int irq, ret;
+ u32 bitrate;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
@@ -489,25 +486,15 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
return -ENOENT;
}
- res_size = resource_size(res);
-
- if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
- dev_err(&pdev->dev, "request_mem_region failed\n");
+ base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!base)
return -EBUSY;
- }
-
- base = ioremap(res->start, res_size);
- if (!base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -EIO;
- goto fail1;
- }
- i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
+ i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct),
+ GFP_KERNEL);
if (!i2c_imx) {
dev_err(&pdev->dev, "can't allocate interface\n");
- ret = -ENOMEM;
- goto fail2;
+ return -ENOMEM;
}
/* Setup i2c_imx driver structure */
@@ -517,29 +504,27 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
- i2c_imx->irq = irq;
i2c_imx->base = base;
- i2c_imx->res = res;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto fail3;
+ dev_err(&pdev->dev, "can't get/select pinctrl\n");
+ return PTR_ERR(pinctrl);
}
/* Get I2C clock */
- i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
+ i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
- ret = PTR_ERR(i2c_imx->clk);
dev_err(&pdev->dev, "can't get I2C clock\n");
- goto fail3;
+ return PTR_ERR(i2c_imx->clk);
}
/* Request IRQ */
- ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
+ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
+ pdev->name, i2c_imx);
if (ret) {
- dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
- goto fail4;
+ dev_err(&pdev->dev, "can't claim irq %d\n", irq);
+ return ret;
}
/* Init queue */
@@ -564,7 +549,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
- goto fail5;
+ return ret;
}
of_i2c_register_devices(&i2c_imx->adapter);
@@ -572,28 +557,16 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
- dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
+ dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
- i2c_imx->res->start, i2c_imx->res->end);
- dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
- res_size, i2c_imx->res->start);
+ res->start, res->end);
+ dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x\n",
+ resource_size(res), res->start);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
return 0; /* Return OK */
-
-fail5:
- free_irq(i2c_imx->irq, i2c_imx);
-fail4:
- clk_put(i2c_imx->clk);
-fail3:
- kfree(i2c_imx);
-fail2:
- iounmap(base);
-fail1:
- release_mem_region(res->start, resource_size(res));
- return ret; /* Return error number */
}
static int __exit i2c_imx_remove(struct platform_device *pdev)
@@ -605,20 +578,12 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c_imx->adapter);
platform_set_drvdata(pdev, NULL);
- /* free interrupt */
- free_irq(i2c_imx->irq, i2c_imx);
-
/* setup chip registers to defaults */
writeb(0, i2c_imx->base + IMX_I2C_IADR);
writeb(0, i2c_imx->base + IMX_I2C_IFDR);
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
- clk_put(i2c_imx->clk);
-
- iounmap(i2c_imx->base);
- release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res));
- kfree(i2c_imx);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 4f44a33017b0..2e9d56719e99 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -18,6 +18,11 @@
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_i2c.h>
+#include <linux/clk.h>
+#include <linux/err.h>
/* Register defines */
#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
@@ -98,6 +103,9 @@ struct mv64xxx_i2c_data {
int rc;
u32 freq_m;
u32 freq_n;
+#if defined(CONFIG_HAVE_CLK)
+ struct clk *clk;
+#endif
wait_queue_head_t waitq;
spinlock_t lock;
struct i2c_msg *msg;
@@ -521,6 +529,82 @@ mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
drv_data->reg_base_p = 0;
}
+#ifdef CONFIG_OF
+static int __devinit
+mv64xxx_calc_freq(const int tclk, const int n, const int m)
+{
+ return tclk / (10 * (m + 1) * (2 << n));
+}
+
+static bool __devinit
+mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
+ u32 *best_m)
+{
+ int freq, delta, best_delta = INT_MAX;
+ int m, n;
+
+ for (n = 0; n <= 7; n++)
+ for (m = 0; m <= 15; m++) {
+ freq = mv64xxx_calc_freq(tclk, n, m);
+ delta = req_freq - freq;
+ if (delta >= 0 && delta < best_delta) {
+ *best_m = m;
+ *best_n = n;
+ best_delta = delta;
+ }
+ if (best_delta == 0)
+ return true;
+ }
+ if (best_delta == INT_MAX)
+ return false;
+ return true;
+}
+
+static int __devinit
+mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
+ struct device_node *np)
+{
+ u32 bus_freq, tclk;
+ int rc = 0;
+
+ /* CLK is mandatory when using DT to describe the i2c bus. We
+ * need to know tclk in order to calculate bus clock
+ * factors.
+ */
+#if !defined(CONFIG_HAVE_CLK)
+ /* Have OF but no CLK */
+ return -ENODEV;
+#else
+ if (IS_ERR(drv_data->clk)) {
+ rc = -ENODEV;
+ goto out;
+ }
+ tclk = clk_get_rate(drv_data->clk);
+ of_property_read_u32(np, "clock-frequency", &bus_freq);
+ if (!mv64xxx_find_baud_factors(bus_freq, tclk,
+ &drv_data->freq_n, &drv_data->freq_m)) {
+ rc = -EINVAL;
+ goto out;
+ }
+ drv_data->irq = irq_of_parse_and_map(np, 0);
+
+ /* Its not yet defined how timeouts will be specified in device tree.
+ * So hard code the value to 1 second.
+ */
+ drv_data->adapter.timeout = HZ;
+out:
+ return rc;
+#endif
+}
+#else /* CONFIG_OF */
+static int __devinit
+mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
+ struct device_node *np)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
static int __devinit
mv64xxx_i2c_probe(struct platform_device *pd)
{
@@ -528,7 +612,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data;
int rc;
- if ((pd->id != 0) || !pdata)
+ if ((!pdata && !pd->dev.of_node))
return -ENODEV;
drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
@@ -546,19 +630,35 @@ mv64xxx_i2c_probe(struct platform_device *pd)
init_waitqueue_head(&drv_data->waitq);
spin_lock_init(&drv_data->lock);
- drv_data->freq_m = pdata->freq_m;
- drv_data->freq_n = pdata->freq_n;
- drv_data->irq = platform_get_irq(pd, 0);
+#if defined(CONFIG_HAVE_CLK)
+ /* Not all platforms have a clk */
+ drv_data->clk = clk_get(&pd->dev, NULL);
+ if (!IS_ERR(drv_data->clk)) {
+ clk_prepare(drv_data->clk);
+ clk_enable(drv_data->clk);
+ }
+#endif
+ if (pdata) {
+ drv_data->freq_m = pdata->freq_m;
+ drv_data->freq_n = pdata->freq_n;
+ drv_data->irq = platform_get_irq(pd, 0);
+ drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
+ } else if (pd->dev.of_node) {
+ rc = mv64xxx_of_config(drv_data, pd->dev.of_node);
+ if (rc)
+ goto exit_unmap_regs;
+ }
if (drv_data->irq < 0) {
rc = -ENXIO;
goto exit_unmap_regs;
}
+
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
- drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout);
drv_data->adapter.nr = pd->id;
+ drv_data->adapter.dev.of_node = pd->dev.of_node;
platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
@@ -577,11 +677,20 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_free_irq;
}
+ of_i2c_register_devices(&drv_data->adapter);
+
return 0;
exit_free_irq:
free_irq(drv_data->irq, drv_data);
exit_unmap_regs:
+#if defined(CONFIG_HAVE_CLK)
+ /* Not all platforms have a clk */
+ if (!IS_ERR(drv_data->clk)) {
+ clk_disable(drv_data->clk);
+ clk_unprepare(drv_data->clk);
+ }
+#endif
mv64xxx_i2c_unmap_regs(drv_data);
exit_kfree:
kfree(drv_data);
@@ -597,17 +706,31 @@ mv64xxx_i2c_remove(struct platform_device *dev)
rc = i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data);
mv64xxx_i2c_unmap_regs(drv_data);
+#if defined(CONFIG_HAVE_CLK)
+ /* Not all platforms have a clk */
+ if (!IS_ERR(drv_data->clk)) {
+ clk_disable(drv_data->clk);
+ clk_unprepare(drv_data->clk);
+ }
+#endif
kfree(drv_data);
return rc;
}
+static const struct of_device_id mv64xxx_i2c_of_match_table[] __devinitdata = {
+ { .compatible = "marvell,mv64xxx-i2c", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
+
static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.remove = __devexit_p(mv64xxx_i2c_remove),
.driver = {
.owner = THIS_MODULE,
.name = MV64XXX_I2C_CTLR_NAME,
+ .of_match_table = of_match_ptr(mv64xxx_i2c_of_match_table),
},
};
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 04eb441b6ce1..088c5c1ed17d 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -46,6 +46,10 @@
#define MXS_I2C_CTRL0_DIRECTION 0x00010000
#define MXS_I2C_CTRL0_XFER_COUNT(v) ((v) & 0x0000FFFF)
+#define MXS_I2C_TIMING0 (0x10)
+#define MXS_I2C_TIMING1 (0x20)
+#define MXS_I2C_TIMING2 (0x30)
+
#define MXS_I2C_CTRL1 (0x40)
#define MXS_I2C_CTRL1_SET (0x44)
#define MXS_I2C_CTRL1_CLR (0x48)
@@ -97,6 +101,35 @@
#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \
MXS_I2C_CTRL0_MASTER_MODE)
+struct mxs_i2c_speed_config {
+ uint32_t timing0;
+ uint32_t timing1;
+ uint32_t timing2;
+};
+
+/*
+ * Timing values for the default 24MHz clock supplied into the i2c block.
+ *
+ * The bus can operate at 95kHz or at 400kHz with the following timing
+ * register configurations. The 100kHz mode isn't present because it's
+ * values are not stated in the i.MX233/i.MX28 datasheet. The 95kHz mode
+ * shall be close enough replacement. Therefore when the bus is configured
+ * for 100kHz operation, 95kHz timing settings are actually loaded.
+ *
+ * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4].
+ */
+static const struct mxs_i2c_speed_config mxs_i2c_95kHz_config = {
+ .timing0 = 0x00780030,
+ .timing1 = 0x00800030,
+ .timing2 = 0x00300030,
+};
+
+static const struct mxs_i2c_speed_config mxs_i2c_400kHz_config = {
+ .timing0 = 0x000f0007,
+ .timing1 = 0x001f000f,
+ .timing2 = 0x00300030,
+};
+
/**
* struct mxs_i2c_dev - per device, private MXS-I2C data
*
@@ -112,11 +145,17 @@ struct mxs_i2c_dev {
struct completion cmd_complete;
u32 cmd_err;
struct i2c_adapter adapter;
+ const struct mxs_i2c_speed_config *speed;
};
static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
{
stmp_reset_block(i2c->regs);
+
+ writel(i2c->speed->timing0, i2c->regs + MXS_I2C_TIMING0);
+ writel(i2c->speed->timing1, i2c->regs + MXS_I2C_TIMING1);
+ writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2);
+
writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
i2c->regs + MXS_I2C_QUEUECTRL_SET);
@@ -193,7 +232,7 @@ static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c)
static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len)
{
- u32 data;
+ u32 uninitialized_var(data);
int i;
for (i = 0; i < len; i++) {
@@ -319,6 +358,28 @@ static const struct i2c_algorithm mxs_i2c_algo = {
.functionality = mxs_i2c_func,
};
+static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
+{
+ uint32_t speed;
+ struct device *dev = i2c->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+
+ if (!node)
+ return -EINVAL;
+
+ i2c->speed = &mxs_i2c_95kHz_config;
+ ret = of_property_read_u32(node, "clock-frequency", &speed);
+ if (ret)
+ dev_warn(dev, "No I2C speed selected, using 100kHz\n");
+ else if (speed == 400000)
+ i2c->speed = &mxs_i2c_400kHz_config;
+ else if (speed != 100000)
+ dev_warn(dev, "Unsupported I2C speed selected, using 100kHz\n");
+
+ return 0;
+}
+
static int __devinit mxs_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -358,6 +419,11 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
return err;
i2c->dev = dev;
+
+ err = mxs_i2c_get_ofdata(i2c);
+ if (err)
+ return err;
+
platform_set_drvdata(pdev, i2c);
/* Do reset to enforce correct startup after pinmuxing */
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index a92440dbef07..5e6f1eed4f83 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -14,7 +14,8 @@
*/
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/atomic.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
@@ -23,8 +24,7 @@
#include <linux/io.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
-
-#include <plat/i2c.h>
+#include <linux/platform_data/i2c-nomadik.h>
#define DRIVER_NAME "nmk-i2c"
@@ -136,7 +136,7 @@ struct i2c_nmk_client {
/**
* struct nmk_i2c_dev - private data structure of the controller.
- * @pdev: parent platform device.
+ * @adev: parent amba device.
* @adap: corresponding I2C adapter.
* @irq: interrupt line for the controller.
* @virtbase: virtual io memory area.
@@ -150,7 +150,7 @@ struct i2c_nmk_client {
* @busy: Busy doing transfer.
*/
struct nmk_i2c_dev {
- struct platform_device *pdev;
+ struct amba_device *adev;
struct i2c_adapter adap;
int irq;
void __iomem *virtbase;
@@ -217,7 +217,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
}
}
- dev_err(&dev->pdev->dev,
+ dev_err(&dev->adev->dev,
"flushing operation timed out giving up after %d attempts",
LOOP_ATTEMPTS);
@@ -276,15 +276,32 @@ exit:
/**
* load_i2c_mcr_reg() - load the MCR register
* @dev: private data of controller
+ * @flags: message flags
*/
-static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev)
+static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev, u16 flags)
{
u32 mcr = 0;
+ unsigned short slave_adr_3msb_bits;
- /* 7-bit address transaction */
- mcr |= GEN_MASK(1, I2C_MCR_AM, 12);
mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1);
+ if (unlikely(flags & I2C_M_TEN)) {
+ /* 10-bit address transaction */
+ mcr |= GEN_MASK(2, I2C_MCR_AM, 12);
+ /*
+ * Get the top 3 bits.
+ * EA10 represents extended address in MCR. This includes
+ * the extension (MSB bits) of the 7 bit address loaded
+ * in A7
+ */
+ slave_adr_3msb_bits = (dev->cli.slave_adr >> 7) & 0x7;
+
+ mcr |= GEN_MASK(slave_adr_3msb_bits, I2C_MCR_EA10, 8);
+ } else {
+ /* 7-bit address transaction */
+ mcr |= GEN_MASK(1, I2C_MCR_AM, 12);
+ }
+
/* start byte procedure not applied */
mcr |= GEN_MASK(0, I2C_MCR_SB, 11);
@@ -364,7 +381,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* and high speed (up to 3.4 Mb/s)
*/
if (dev->cfg.sm > I2C_FREQ_MODE_FAST) {
- dev_err(&dev->pdev->dev,
+ dev_err(&dev->adev->dev,
"do not support this mode defaulting to std. mode\n");
brcr2 = i2c_clk/(100000 * 2) & 0xffff;
writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
@@ -381,19 +398,20 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
/**
* read_i2c() - Read from I2C client device
* @dev: private data of I2C Driver
+ * @flags: message flags
*
* This function reads from i2c client device when controller is in
* master mode. There is a completion timeout. If there is no transfer
* before timeout error is returned.
*/
-static int read_i2c(struct nmk_i2c_dev *dev)
+static int read_i2c(struct nmk_i2c_dev *dev, u16 flags)
{
u32 status = 0;
u32 mcr;
u32 irq_mask = 0;
int timeout;
- mcr = load_i2c_mcr_reg(dev);
+ mcr = load_i2c_mcr_reg(dev, flags);
writel(mcr, dev->virtbase + I2C_MCR);
/* load the current CR value */
@@ -423,7 +441,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
&dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
- dev_err(&dev->pdev->dev,
+ dev_err(&dev->adev->dev,
"wait_for_completion_timeout "
"returned %d waiting for event\n", timeout);
status = timeout;
@@ -431,7 +449,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
if (timeout == 0) {
/* Controller timed out */
- dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
+ dev_err(&dev->adev->dev, "read from slave 0x%x timed out\n",
dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -459,17 +477,18 @@ static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
/**
* write_i2c() - Write data to I2C client.
* @dev: private data of I2C Driver
+ * @flags: message flags
*
* This function writes data to I2C client
*/
-static int write_i2c(struct nmk_i2c_dev *dev)
+static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
{
u32 status = 0;
u32 mcr;
u32 irq_mask = 0;
int timeout;
- mcr = load_i2c_mcr_reg(dev);
+ mcr = load_i2c_mcr_reg(dev, flags);
writel(mcr, dev->virtbase + I2C_MCR);
@@ -510,7 +529,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
&dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
- dev_err(&dev->pdev->dev,
+ dev_err(&dev->adev->dev,
"wait_for_completion_timeout "
"returned %d waiting for event\n", timeout);
status = timeout;
@@ -518,7 +537,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
if (timeout == 0) {
/* Controller timed out */
- dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
+ dev_err(&dev->adev->dev, "write to slave 0x%x timed out\n",
dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -538,11 +557,11 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
if (flags & I2C_M_RD) {
/* read operation */
dev->cli.operation = I2C_READ;
- status = read_i2c(dev);
+ status = read_i2c(dev, flags);
} else {
/* write operation */
dev->cli.operation = I2C_WRITE;
- status = write_i2c(dev);
+ status = write_i2c(dev, flags);
}
if (status || (dev->result)) {
@@ -557,7 +576,7 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
if (((i2c_sr >> 2) & 0x3) == 0x3) {
/* get the abort cause */
cause = (i2c_sr >> 4) & 0x7;
- dev_err(&dev->pdev->dev, "%s\n",
+ dev_err(&dev->adev->dev, "%s\n",
cause >= ARRAY_SIZE(abort_causes) ?
"unknown reason" :
abort_causes[cause]);
@@ -630,7 +649,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
if (dev->regulator)
regulator_enable(dev->regulator);
- pm_runtime_get_sync(&dev->pdev->dev);
+ pm_runtime_get_sync(&dev->adev->dev);
clk_enable(dev->clk);
@@ -644,13 +663,6 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
setup_i2c_controller(dev);
for (i = 0; i < num_msgs; i++) {
- if (unlikely(msgs[i].flags & I2C_M_TEN)) {
- dev_err(&dev->pdev->dev,
- "10 bit addressing not supported\n");
-
- status = -EINVAL;
- goto out;
- }
dev->cli.slave_adr = msgs[i].addr;
dev->cli.buffer = msgs[i].buf;
dev->cli.count = msgs[i].len;
@@ -667,7 +679,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
out:
clk_disable(dev->clk);
- pm_runtime_put_sync(&dev->pdev->dev);
+ pm_runtime_put_sync(&dev->adev->dev);
if (dev->regulator)
regulator_disable(dev->regulator);
@@ -790,7 +802,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
if (dev->cli.count) {
dev->result = -EIO;
- dev_err(&dev->pdev->dev,
+ dev_err(&dev->adev->dev,
"%lu bytes still remain to be xfered\n",
dev->cli.count);
(void) init_hw(dev);
@@ -834,7 +846,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
dev->result = -EIO;
(void) init_hw(dev);
- dev_err(&dev->pdev->dev, "Tx Fifo Over run\n");
+ dev_err(&dev->adev->dev, "Tx Fifo Over run\n");
complete(&dev->xfer_complete);
break;
@@ -847,10 +859,10 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
case I2C_IT_RFSE:
case I2C_IT_WTSR:
case I2C_IT_STD:
- dev_err(&dev->pdev->dev, "unhandled Interrupt\n");
+ dev_err(&dev->adev->dev, "unhandled Interrupt\n");
break;
default:
- dev_err(&dev->pdev->dev, "spurious Interrupt..\n");
+ dev_err(&dev->adev->dev, "spurious Interrupt..\n");
break;
}
@@ -861,8 +873,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
#ifdef CONFIG_PM
static int nmk_i2c_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+ struct amba_device *adev = to_amba_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
if (nmk_i2c->busy)
return -EBUSY;
@@ -891,7 +903,7 @@ static const struct dev_pm_ops nmk_i2c_pm = {
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
}
static const struct i2c_algorithm nmk_i2c_algo = {
@@ -899,78 +911,69 @@ static const struct i2c_algorithm nmk_i2c_algo = {
.functionality = nmk_i2c_functionality
};
-static int __devinit nmk_i2c_probe(struct platform_device *pdev)
+static atomic_t adapter_id = ATOMIC_INIT(0);
+
+static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
- struct resource *res;
struct nmk_i2c_controller *pdata =
- pdev->dev.platform_data;
+ adev->dev.platform_data;
struct nmk_i2c_dev *dev;
struct i2c_adapter *adap;
+ if (!pdata) {
+ dev_warn(&adev->dev, "no platform data\n");
+ return -ENODEV;
+ }
dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
if (!dev) {
- dev_err(&pdev->dev, "cannot allocate memory\n");
+ dev_err(&adev->dev, "cannot allocate memory\n");
ret = -ENOMEM;
goto err_no_mem;
}
dev->busy = false;
- dev->pdev = pdev;
- platform_set_drvdata(pdev, dev);
+ dev->adev = adev;
+ amba_set_drvdata(adev, dev);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto err_no_resource;
- }
-
- if (request_mem_region(res->start, resource_size(res),
- DRIVER_NAME "I/O region") == NULL) {
- ret = -EBUSY;
- goto err_no_region;
- }
-
- dev->virtbase = ioremap(res->start, resource_size(res));
+ dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
if (!dev->virtbase) {
ret = -ENOMEM;
goto err_no_ioremap;
}
- dev->irq = platform_get_irq(pdev, 0);
+ dev->irq = adev->irq[0];
ret = request_irq(dev->irq, i2c_irq_handler, 0,
DRIVER_NAME, dev);
if (ret) {
- dev_err(&pdev->dev, "cannot claim the irq %d\n", dev->irq);
+ dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq);
goto err_irq;
}
- dev->regulator = regulator_get(&pdev->dev, "v-i2c");
+ dev->regulator = regulator_get(&adev->dev, "v-i2c");
if (IS_ERR(dev->regulator)) {
- dev_warn(&pdev->dev, "could not get i2c regulator\n");
+ dev_warn(&adev->dev, "could not get i2c regulator\n");
dev->regulator = NULL;
}
- pm_suspend_ignore_children(&pdev->dev, true);
- pm_runtime_enable(&pdev->dev);
+ pm_suspend_ignore_children(&adev->dev, true);
- dev->clk = clk_get(&pdev->dev, NULL);
+ dev->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(dev->clk)) {
- dev_err(&pdev->dev, "could not get i2c clock\n");
+ dev_err(&adev->dev, "could not get i2c clock\n");
ret = PTR_ERR(dev->clk);
goto err_no_clk;
}
adap = &dev->adap;
- adap->dev.parent = &pdev->dev;
+ adap->dev.parent = &adev->dev;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->algo = &nmk_i2c_algo;
adap->timeout = msecs_to_jiffies(pdata->timeout);
+ adap->nr = atomic_read(&adapter_id);
snprintf(adap->name, sizeof(adap->name),
- "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start);
-
- /* fetch the controller id */
- adap->nr = pdev->id;
+ "Nomadik I2C%d at %pR", adap->nr, &adev->res);
+ atomic_inc(&adapter_id);
/* fetch the controller configuration from machine */
dev->cfg.clk_freq = pdata->clk_freq;
@@ -981,16 +984,18 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
- dev_info(&pdev->dev,
+ dev_info(&adev->dev,
"initialize %s on virtual base %p\n",
adap->name, dev->virtbase);
ret = i2c_add_numbered_adapter(adap);
if (ret) {
- dev_err(&pdev->dev, "failed to add adapter\n");
+ dev_err(&adev->dev, "failed to add adapter\n");
goto err_add_adap;
}
+ pm_runtime_put(&adev->dev);
+
return 0;
err_add_adap:
@@ -998,25 +1003,21 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
err_no_clk:
if (dev->regulator)
regulator_put(dev->regulator);
- pm_runtime_disable(&pdev->dev);
free_irq(dev->irq, dev);
err_irq:
iounmap(dev->virtbase);
err_no_ioremap:
- release_mem_region(res->start, resource_size(res));
- err_no_region:
- platform_set_drvdata(pdev, NULL);
- err_no_resource:
+ amba_set_drvdata(adev, NULL);
kfree(dev);
err_no_mem:
return ret;
}
-static int __devexit nmk_i2c_remove(struct platform_device *pdev)
+static int nmk_i2c_remove(struct amba_device *adev)
{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct nmk_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *res = &adev->res;
+ struct nmk_i2c_dev *dev = amba_get_drvdata(adev);
i2c_del_adapter(&dev->adap);
flush_i2c_fifo(dev);
@@ -1031,31 +1032,46 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
clk_put(dev->clk);
if (dev->regulator)
regulator_put(dev->regulator);
- pm_runtime_disable(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
+ pm_runtime_disable(&adev->dev);
+ amba_set_drvdata(adev, NULL);
kfree(dev);
return 0;
}
-static struct platform_driver nmk_i2c_driver = {
- .driver = {
+static struct amba_id nmk_i2c_ids[] = {
+ {
+ .id = 0x00180024,
+ .mask = 0x00ffffff,
+ },
+ {
+ .id = 0x00380024,
+ .mask = 0x00ffffff,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(amba, nmk_i2c_ids);
+
+static struct amba_driver nmk_i2c_driver = {
+ .drv = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.pm = &nmk_i2c_pm,
},
+ .id_table = nmk_i2c_ids,
.probe = nmk_i2c_probe,
- .remove = __devexit_p(nmk_i2c_remove),
+ .remove = nmk_i2c_remove,
};
static int __init nmk_i2c_init(void)
{
- return platform_driver_register(&nmk_i2c_driver);
+ return amba_driver_register(&nmk_i2c_driver);
}
static void __exit nmk_i2c_exit(void)
{
- platform_driver_unregister(&nmk_i2c_driver);
+ amba_driver_unregister(&nmk_i2c_driver);
}
subsys_initcall(nmk_i2c_init);
@@ -1064,4 +1080,3 @@ module_exit(nmk_i2c_exit);
MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR");
MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 75194c579b6d..bffd5501ac2d 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -10,40 +10,9 @@
*/
/*
- * Device tree configuration:
- *
- * Required properties:
- * - compatible : "opencores,i2c-ocores"
- * - reg : bus address start and address range size of device
- * - interrupts : interrupt number
- * - regstep : size of device registers in bytes
- * - clock-frequency : frequency of bus clock in Hz
- *
- * Example:
- *
- * i2c0: ocores@a0000000 {
- * compatible = "opencores,i2c-ocores";
- * reg = <0xa0000000 0x8>;
- * interrupts = <10>;
- *
- * regstep = <1>;
- * clock-frequency = <20000000>;
- *
- * -- Devices connected on this I2C bus get
- * -- defined here; address- and size-cells
- * -- apply to these child devices
- *
- * #address-cells = <1>;
- * #size-cells = <0>;
- *
- * dummy@60 {
- * compatible = "dummy";
- * reg = <60>;
- * };
- * };
- *
+ * This driver can be used from the device tree, see
+ * Documentation/devicetree/bindings/i2c/ocore-i2c.txt
*/
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -56,10 +25,12 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of_i2c.h>
+#include <linux/log2.h>
struct ocores_i2c {
void __iomem *base;
- int regstep;
+ u32 reg_shift;
+ u32 reg_io_width;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *msg;
@@ -102,12 +73,22 @@ struct ocores_i2c {
static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
{
- iowrite8(value, i2c->base + reg * i2c->regstep);
+ if (i2c->reg_io_width == 4)
+ iowrite32(value, i2c->base + (reg << i2c->reg_shift));
+ else if (i2c->reg_io_width == 2)
+ iowrite16(value, i2c->base + (reg << i2c->reg_shift));
+ else
+ iowrite8(value, i2c->base + (reg << i2c->reg_shift));
}
static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
{
- return ioread8(i2c->base + reg * i2c->regstep);
+ if (i2c->reg_io_width == 4)
+ return ioread32(i2c->base + (reg << i2c->reg_shift));
+ else if (i2c->reg_io_width == 2)
+ return ioread16(i2c->base + (reg << i2c->reg_shift));
+ else
+ return ioread8(i2c->base + (reg << i2c->reg_shift));
}
static void ocores_process(struct ocores_i2c *i2c)
@@ -247,26 +228,35 @@ static struct i2c_adapter ocores_adapter = {
};
#ifdef CONFIG_OF
-static int ocores_i2c_of_probe(struct platform_device* pdev,
- struct ocores_i2c* i2c)
+static int ocores_i2c_of_probe(struct platform_device *pdev,
+ struct ocores_i2c *i2c)
{
- const __be32* val;
-
- val = of_get_property(pdev->dev.of_node, "regstep", NULL);
- if (!val) {
- dev_err(&pdev->dev, "Missing required parameter 'regstep'");
- return -ENODEV;
+ struct device_node *np = pdev->dev.of_node;
+ u32 val;
+
+ if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) {
+ /* no 'reg-shift', check for deprecated 'regstep' */
+ if (!of_property_read_u32(np, "regstep", &val)) {
+ if (!is_power_of_2(val)) {
+ dev_err(&pdev->dev, "invalid regstep %d\n",
+ val);
+ return -EINVAL;
+ }
+ i2c->reg_shift = ilog2(val);
+ dev_warn(&pdev->dev,
+ "regstep property deprecated, use reg-shift\n");
+ }
}
- i2c->regstep = be32_to_cpup(val);
- val = of_get_property(pdev->dev.of_node, "clock-frequency", NULL);
- if (!val) {
+ if (of_property_read_u32(np, "clock-frequency", &val)) {
dev_err(&pdev->dev,
- "Missing required parameter 'clock-frequency'");
+ "Missing required parameter 'clock-frequency'\n");
return -ENODEV;
}
- i2c->clock_khz = be32_to_cpup(val) / 1000;
+ i2c->clock_khz = val / 1000;
+ of_property_read_u32(pdev->dev.of_node, "reg-io-width",
+ &i2c->reg_io_width);
return 0;
}
#else
@@ -308,7 +298,8 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
if (pdata) {
- i2c->regstep = pdata->regstep;
+ i2c->reg_shift = pdata->reg_shift;
+ i2c->reg_io_width = pdata->reg_io_width;
i2c->clock_khz = pdata->clock_khz;
} else {
ret = ocores_i2c_of_probe(pdev, i2c);
@@ -316,6 +307,9 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
return ret;
}
+ if (i2c->reg_io_width == 0)
+ i2c->reg_io_width = 1; /* Set to default value */
+
ocores_init(i2c);
init_waitqueue_head(&i2c->wait);
@@ -351,7 +345,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit ocores_i2c_remove(struct platform_device* pdev)
+static int __devexit ocores_i2c_remove(struct platform_device *pdev)
{
struct ocores_i2c *i2c = platform_get_drvdata(pdev);
@@ -367,9 +361,9 @@ static int __devexit ocores_i2c_remove(struct platform_device* pdev)
}
#ifdef CONFIG_PM
-static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+static int ocores_i2c_suspend(struct device *dev)
{
- struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+ struct ocores_i2c *i2c = dev_get_drvdata(dev);
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
/* make sure the device is disabled */
@@ -378,17 +372,19 @@ static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int ocores_i2c_resume(struct platform_device *pdev)
+static int ocores_i2c_resume(struct device *dev)
{
- struct ocores_i2c *i2c = platform_get_drvdata(pdev);
+ struct ocores_i2c *i2c = dev_get_drvdata(dev);
ocores_init(i2c);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume);
+#define OCORES_I2C_PM (&ocores_i2c_pm)
#else
-#define ocores_i2c_suspend NULL
-#define ocores_i2c_resume NULL
+#define OCORES_I2C_PM NULL
#endif
static struct of_device_id ocores_i2c_match[] = {
@@ -400,12 +396,11 @@ MODULE_DEVICE_TABLE(of, ocores_i2c_match);
static struct platform_driver ocores_i2c_driver = {
.probe = ocores_i2c_probe,
.remove = __devexit_p(ocores_i2c_remove),
- .suspend = ocores_i2c_suspend,
- .resume = ocores_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "ocores-i2c",
.of_match_table = ocores_i2c_match,
+ .pm = OCORES_I2C_PM,
},
};
diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c
index ee139a598814..f44c83549fe5 100644
--- a/drivers/i2c/busses/i2c-octeon.c
+++ b/drivers/i2c/busses/i2c-octeon.c
@@ -2,7 +2,7 @@
* (C) Copyright 2009-2010
* Nokia Siemens Networks, michael.lawnick.ext@nsn.com
*
- * Portions Copyright (C) 2010 Cavium Networks, Inc.
+ * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc.
*
* This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
*
@@ -11,17 +11,18 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_i2c.h>
+#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
-
-#include <linux/io.h>
#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
#include <asm/octeon/octeon.h>
@@ -65,7 +66,7 @@ struct octeon_i2c {
wait_queue_head_t queue;
struct i2c_adapter adap;
int irq;
- int twsi_freq;
+ u32 twsi_freq;
int sys_freq;
resource_size_t twsi_phys;
void __iomem *twsi_base;
@@ -121,10 +122,8 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg)
*/
static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
{
- u64 tmp;
-
__raw_writeq(data, i2c->twsi_base + TWSI_INT);
- tmp = __raw_readq(i2c->twsi_base + TWSI_INT);
+ __raw_readq(i2c->twsi_base + TWSI_INT);
}
/**
@@ -515,7 +514,6 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev)
{
int irq, result = 0;
struct octeon_i2c *i2c;
- struct octeon_i2c_data *i2c_data;
struct resource *res_mem;
/* All adaptors have an irq. */
@@ -523,86 +521,90 @@ static int __devinit octeon_i2c_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "kzalloc failed\n");
result = -ENOMEM;
goto out;
}
i2c->dev = &pdev->dev;
- i2c_data = pdev->dev.platform_data;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_mem == NULL) {
dev_err(i2c->dev, "found no memory resource\n");
result = -ENXIO;
- goto fail_region;
+ goto out;
}
+ i2c->twsi_phys = res_mem->start;
+ i2c->regsize = resource_size(res_mem);
- if (i2c_data == NULL) {
- dev_err(i2c->dev, "no I2C frequency data\n");
+ /*
+ * "clock-rate" is a legacy binding, the official binding is
+ * "clock-frequency". Try the official one first and then
+ * fall back if it doesn't exist.
+ */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &i2c->twsi_freq) &&
+ of_property_read_u32(pdev->dev.of_node,
+ "clock-rate", &i2c->twsi_freq)) {
+ dev_err(i2c->dev,
+ "no I2C 'clock-rate' or 'clock-frequency' property\n");
result = -ENXIO;
- goto fail_region;
+ goto out;
}
- i2c->twsi_phys = res_mem->start;
- i2c->regsize = resource_size(res_mem);
- i2c->twsi_freq = i2c_data->i2c_freq;
- i2c->sys_freq = i2c_data->sys_freq;
+ i2c->sys_freq = octeon_get_io_clock_rate();
- if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) {
+ if (!devm_request_mem_region(&pdev->dev, i2c->twsi_phys, i2c->regsize,
+ res_mem->name)) {
dev_err(i2c->dev, "request_mem_region failed\n");
- goto fail_region;
+ goto out;
}
- i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize);
+ i2c->twsi_base = devm_ioremap(&pdev->dev, i2c->twsi_phys, i2c->regsize);
init_waitqueue_head(&i2c->queue);
i2c->irq = irq;
- result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c);
+ result = devm_request_irq(&pdev->dev, i2c->irq,
+ octeon_i2c_isr, 0, DRV_NAME, i2c);
if (result < 0) {
dev_err(i2c->dev, "failed to attach interrupt\n");
- goto fail_irq;
+ goto out;
}
result = octeon_i2c_initlowlevel(i2c);
if (result) {
dev_err(i2c->dev, "init low level failed\n");
- goto fail_add;
+ goto out;
}
result = octeon_i2c_setclock(i2c);
if (result) {
dev_err(i2c->dev, "clock init failed\n");
- goto fail_add;
+ goto out;
}
i2c->adap = octeon_i2c_ops;
i2c->adap.dev.parent = &pdev->dev;
- i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0;
+ i2c->adap.dev.of_node = pdev->dev.of_node;
i2c_set_adapdata(&i2c->adap, i2c);
platform_set_drvdata(pdev, i2c);
- result = i2c_add_numbered_adapter(&i2c->adap);
+ result = i2c_add_adapter(&i2c->adap);
if (result < 0) {
dev_err(i2c->dev, "failed to add adapter\n");
goto fail_add;
}
-
dev_info(i2c->dev, "version %s\n", DRV_VERSION);
- return result;
+ of_i2c_register_devices(&i2c->adap);
+
+ return 0;
fail_add:
platform_set_drvdata(pdev, NULL);
- free_irq(i2c->irq, i2c);
-fail_irq:
- iounmap(i2c->twsi_base);
- release_mem_region(i2c->twsi_phys, i2c->regsize);
-fail_region:
- kfree(i2c);
out:
return result;
};
@@ -613,19 +615,24 @@ static int __devexit octeon_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&i2c->adap);
platform_set_drvdata(pdev, NULL);
- free_irq(i2c->irq, i2c);
- iounmap(i2c->twsi_base);
- release_mem_region(i2c->twsi_phys, i2c->regsize);
- kfree(i2c);
return 0;
};
+static struct of_device_id octeon_i2c_match[] = {
+ {
+ .compatible = "cavium,octeon-3860-twsi",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, octeon_i2c_match);
+
static struct platform_driver octeon_i2c_driver = {
.probe = octeon_i2c_probe,
.remove = __devexit_p(octeon_i2c_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRV_NAME,
+ .of_match_table = octeon_i2c_match,
},
};
@@ -635,4 +642,3 @@ MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index c2148332de0f..6849635b268a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -49,8 +49,8 @@
/* I2C controller revisions present on specific hardware */
#define OMAP_I2C_REV_ON_2430 0x36
-#define OMAP_I2C_REV_ON_3430 0x3C
-#define OMAP_I2C_REV_ON_3530_4430 0x40
+#define OMAP_I2C_REV_ON_3430_3530 0x3C
+#define OMAP_I2C_REV_ON_3630_4430 0x40
/* timeout waiting for the controller to respond */
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
@@ -173,7 +173,7 @@ enum {
/* Errata definitions */
#define I2C_OMAP_ERRATA_I207 (1 << 0)
-#define I2C_OMAP3_1P153 (1 << 1)
+#define I2C_OMAP_ERRATA_I462 (1 << 1)
struct omap_i2c_dev {
struct device *dev;
@@ -269,47 +269,6 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
(i2c_dev->regs[reg] << i2c_dev->reg_shift));
}
-static void omap_i2c_unidle(struct omap_i2c_dev *dev)
-{
- if (dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
- omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
- omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
- omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
- omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate);
- omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate);
- omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate);
- omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
- omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
- }
-
- /*
- * Don't write to this register if the IE state is 0 as it can
- * cause deadlock.
- */
- if (dev->iestate)
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
-}
-
-static void omap_i2c_idle(struct omap_i2c_dev *dev)
-{
- u16 iv;
-
- dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
- if (dev->dtrev == OMAP_I2C_IP_VERSION_2)
- omap_i2c_write_reg(dev, OMAP_I2C_IP_V2_IRQENABLE_CLR, 1);
- else
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
-
- if (dev->rev < OMAP_I2C_OMAP1_REV_2) {
- iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */
- } else {
- omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate);
-
- /* Flush posted write */
- omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
- }
-}
-
static int omap_i2c_init(struct omap_i2c_dev *dev)
{
u16 psc = 0, scll = 0, sclh = 0, buf = 0;
@@ -346,7 +305,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG,
SYSC_AUTOIDLE_MASK);
- } else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
+ } else if (dev->rev >= OMAP_I2C_REV_ON_3430_3530) {
dev->syscstate = SYSC_AUTOIDLE_MASK;
dev->syscstate |= SYSC_ENAWAKEUP_MASK;
dev->syscstate |= (SYSC_IDLEMODE_SMART <<
@@ -468,11 +427,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
/* Take the I2C module out of reset: */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
- dev->errata = 0;
-
- if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
- dev->errata |= I2C_OMAP_ERRATA_I207;
-
/* Enable interrupts */
dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
@@ -514,7 +468,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
struct i2c_msg *msg, int stop)
{
struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
- int r;
+ unsigned long timeout;
u16 w;
dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
@@ -536,7 +490,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w);
- init_completion(&dev->cmd_complete);
+ INIT_COMPLETION(dev->cmd_complete);
dev->cmd_err = 0;
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@@ -584,12 +538,10 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* REVISIT: We should abort the transfer on signals, but the bus goes
* into arbitration and we're currently unable to recover from it.
*/
- r = wait_for_completion_timeout(&dev->cmd_complete,
- OMAP_I2C_TIMEOUT);
+ timeout = wait_for_completion_timeout(&dev->cmd_complete,
+ OMAP_I2C_TIMEOUT);
dev->buf_len = 0;
- if (r < 0)
- return r;
- if (r == 0) {
+ if (timeout == 0) {
dev_err(dev->dev, "controller timed out\n");
omap_i2c_init(dev);
return -ETIMEDOUT;
@@ -630,7 +582,9 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
int i;
int r;
- pm_runtime_get_sync(dev->dev);
+ r = pm_runtime_get_sync(dev->dev);
+ if (IS_ERR_VALUE(r))
+ return r;
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
@@ -767,11 +721,11 @@ omap_i2c_omap1_isr(int this_irq, void *dev_id)
#endif
/*
- * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing
+ * OMAP3430 Errata i462: When an XRDY/XDR is hit, wait for XUDF before writing
* data to DATA_REG. Otherwise some data bytes can be lost while transferring
* them from the memory to the I2C interface.
*/
-static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
+static int errata_omap3_i462(struct omap_i2c_dev *dev, u16 *stat, int *err)
{
unsigned long timeout = 10000;
@@ -779,7 +733,6 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY |
OMAP_I2C_STAT_XDR));
- *err |= OMAP_I2C_STAT_XUDF;
return -ETIMEDOUT;
}
@@ -792,6 +745,7 @@ static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
return 0;
}
+ *err |= OMAP_I2C_STAT_XUDF;
return 0;
}
@@ -930,8 +884,8 @@ complete:
break;
}
- if ((dev->errata & I2C_OMAP3_1P153) &&
- errata_omap3_1p153(dev, &stat, &err))
+ if ((dev->errata & I2C_OMAP_ERRATA_I462) &&
+ errata_omap3_i462(dev, &stat, &err))
goto complete;
omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
@@ -1048,6 +1002,7 @@ omap_i2c_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, dev);
+ init_completion(&dev->cmd_complete);
dev->reg_shift = (dev->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;
@@ -1057,12 +1012,19 @@ omap_i2c_probe(struct platform_device *pdev)
dev->regs = (u8 *)reg_map_ip_v1;
pm_runtime_enable(dev->dev);
- pm_runtime_get_sync(dev->dev);
+ r = pm_runtime_get_sync(dev->dev);
+ if (IS_ERR_VALUE(r))
+ goto err_free_mem;
dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
- if (dev->rev <= OMAP_I2C_REV_ON_3430)
- dev->errata |= I2C_OMAP3_1P153;
+ dev->errata = 0;
+
+ if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207)
+ dev->errata |= I2C_OMAP_ERRATA_I207;
+
+ if (dev->rev <= OMAP_I2C_REV_ON_3430_3530)
+ dev->errata |= I2C_OMAP_ERRATA_I462;
if (!(dev->flags & OMAP_I2C_FLAG_NO_FIFO)) {
u16 s;
@@ -1079,7 +1041,7 @@ omap_i2c_probe(struct platform_device *pdev)
dev->fifo_size = (dev->fifo_size / 2);
- if (dev->rev >= OMAP_I2C_REV_ON_3530_4430)
+ if (dev->rev >= OMAP_I2C_REV_ON_3630_4430)
dev->b_hw = 0; /* Disable hardware fixes */
else
dev->b_hw = 1; /* Enable hardware fixes */
@@ -1095,7 +1057,7 @@ omap_i2c_probe(struct platform_device *pdev)
isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
omap_i2c_isr;
- r = request_irq(dev->irq, isr, 0, pdev->name, dev);
+ r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
if (r) {
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
@@ -1105,8 +1067,6 @@ omap_i2c_probe(struct platform_device *pdev)
dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
- pm_runtime_put(dev->dev);
-
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
@@ -1126,6 +1086,8 @@ omap_i2c_probe(struct platform_device *pdev)
of_i2c_register_devices(adap);
+ pm_runtime_put(dev->dev);
+
return 0;
err_free_irq:
@@ -1134,6 +1096,7 @@ err_unuse_clocks:
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
pm_runtime_put(dev->dev);
iounmap(dev->base);
+ pm_runtime_disable(&pdev->dev);
err_free_mem:
platform_set_drvdata(pdev, NULL);
kfree(dev);
@@ -1143,17 +1106,23 @@ err_release_region:
return r;
}
-static int
-omap_i2c_remove(struct platform_device *pdev)
+static int __devexit omap_i2c_remove(struct platform_device *pdev)
{
struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
struct resource *mem;
+ int ret;
platform_set_drvdata(pdev, NULL);
free_irq(dev->irq, dev);
i2c_del_adapter(&dev->adapter);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
iounmap(dev->base);
kfree(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1161,13 +1130,26 @@ omap_i2c_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
#ifdef CONFIG_PM_RUNTIME
static int omap_i2c_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct omap_i2c_dev *_dev = platform_get_drvdata(pdev);
+ u16 iv;
+
+ _dev->iestate = omap_i2c_read_reg(_dev, OMAP_I2C_IE_REG);
+
+ omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, 0);
- omap_i2c_idle(_dev);
+ if (_dev->rev < OMAP_I2C_OMAP1_REV_2) {
+ iv = omap_i2c_read_reg(_dev, OMAP_I2C_IV_REG); /* Read clears */
+ } else {
+ omap_i2c_write_reg(_dev, OMAP_I2C_STAT_REG, _dev->iestate);
+
+ /* Flush posted write */
+ omap_i2c_read_reg(_dev, OMAP_I2C_STAT_REG);
+ }
return 0;
}
@@ -1177,23 +1159,40 @@ 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);
- omap_i2c_unidle(_dev);
+ if (_dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
+ omap_i2c_write_reg(_dev, OMAP_I2C_CON_REG, 0);
+ omap_i2c_write_reg(_dev, OMAP_I2C_PSC_REG, _dev->pscstate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_SCLL_REG, _dev->scllstate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_SCLH_REG, _dev->sclhstate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_BUF_REG, _dev->bufstate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_SYSC_REG, _dev->syscstate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_WE_REG, _dev->westate);
+ omap_i2c_write_reg(_dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ }
+
+ /*
+ * Don't write to this register if the IE state is 0 as it can
+ * cause deadlock.
+ */
+ if (_dev->iestate)
+ omap_i2c_write_reg(_dev, OMAP_I2C_IE_REG, _dev->iestate);
return 0;
}
+#endif /* CONFIG_PM_RUNTIME */
static struct dev_pm_ops omap_i2c_pm_ops = {
- .runtime_suspend = omap_i2c_runtime_suspend,
- .runtime_resume = omap_i2c_runtime_resume,
+ SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
+ omap_i2c_runtime_resume, NULL)
};
#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
#else
#define OMAP_I2C_PM_OPS NULL
-#endif
+#endif /* CONFIG_PM */
static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
- .remove = omap_i2c_remove,
+ .remove = __devexit_p(omap_i2c_remove),
.driver = {
.name = "omap_i2c",
.owner = THIS_MODULE,
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index 07b7447ecbc9..3d71395ae1f7 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -306,8 +306,7 @@ static int __devinit pmcmsptwi_probe(struct platform_device *pldev)
pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
if (pmcmsptwi_data.irq) {
rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- pldev->name, &pmcmsptwi_data);
+ IRQF_SHARED, pldev->name, &pmcmsptwi_data);
if (rc == 0) {
/*
* Enable 'DONE' interrupt only.
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index 99389d2eae51..5d54416770b0 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -587,25 +587,27 @@ static struct i2c_algorithm pnx_algorithm = {
};
#ifdef CONFIG_PM
-static int i2c_pnx_controller_suspend(struct platform_device *pdev,
- pm_message_t state)
+static int i2c_pnx_controller_suspend(struct device *dev)
{
- struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+ struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
clk_disable(alg_data->clk);
return 0;
}
-static int i2c_pnx_controller_resume(struct platform_device *pdev)
+static int i2c_pnx_controller_resume(struct device *dev)
{
- struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
+ struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
return clk_enable(alg_data->clk);
}
+
+static SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
+ i2c_pnx_controller_suspend, i2c_pnx_controller_resume);
+#define PNX_I2C_PM (&i2c_pnx_pm)
#else
-#define i2c_pnx_controller_suspend NULL
-#define i2c_pnx_controller_resume NULL
+#define PNX_I2C_PM NULL
#endif
static int __devinit i2c_pnx_probe(struct platform_device *pdev)
@@ -783,11 +785,10 @@ static struct platform_driver i2c_pnx_driver = {
.name = "pnx-i2c",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(i2c_pnx_of_match),
+ .pm = PNX_I2C_PM,
},
.probe = i2c_pnx_probe,
.remove = __devexit_p(i2c_pnx_remove),
- .suspend = i2c_pnx_controller_suspend,
- .resume = i2c_pnx_controller_resume,
};
static int __init i2c_adap_pnx_init(void)
diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c
index 93709fbe30eb..d8515be00b98 100644
--- a/drivers/i2c/busses/i2c-puv3.c
+++ b/drivers/i2c/busses/i2c-puv3.c
@@ -254,7 +254,7 @@ static int __devexit puv3_i2c_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state)
+static int puv3_i2c_suspend(struct device *dev)
{
int poll_count;
/* Disable the IIC */
@@ -267,23 +267,20 @@ static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state)
return 0;
}
-static int puv3_i2c_resume(struct platform_device *dev)
-{
- return 0 ;
-}
+static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL);
+#define PUV3_I2C_PM (&puv3_i2c_pm)
+
#else
-#define puv3_i2c_suspend NULL
-#define puv3_i2c_resume NULL
+#define PUV3_I2C_PM NULL
#endif
static struct platform_driver puv3_i2c_driver = {
.probe = puv3_i2c_probe,
.remove = __devexit_p(puv3_i2c_remove),
- .suspend = puv3_i2c_suspend,
- .resume = puv3_i2c_resume,
.driver = {
.name = "PKUnity-v3-I2C",
.owner = THIS_MODULE,
+ .pm = PUV3_I2C_PM,
}
};
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index a997c7d3f95d..1034d93fb838 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -41,13 +41,6 @@
#include <asm/irq.h>
-#ifndef CONFIG_HAVE_CLK
-#define clk_get(dev, id) NULL
-#define clk_put(clk) do { } while (0)
-#define clk_disable(clk) do { } while (0)
-#define clk_enable(clk) do { } while (0)
-#endif
-
struct pxa_reg_layout {
u32 ibmr;
u32 idbr;
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 01959154572d..5ae3b0236bd3 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -122,7 +122,7 @@ static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pde
{
if (pdev->dev.of_node) {
const struct of_device_id *match;
- match = of_match_node(&s3c24xx_i2c_match, pdev->dev.of_node);
+ match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
return (unsigned int)match->data;
}
@@ -609,7 +609,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
if (ret != -EAGAIN) {
clk_disable(i2c->clk);
- pm_runtime_put_sync(&adap->dev);
+ pm_runtime_put(&adap->dev);
return ret;
}
@@ -619,7 +619,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
}
clk_disable(i2c->clk);
- pm_runtime_put_sync(&adap->dev);
+ pm_runtime_put(&adap->dev);
return -EREMOTEIO;
}
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 4d44af181f37..580a0c04cb42 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2009 ST-Ericsson AB
+ * Copyright (C) 2007-2012 ST-Ericsson AB
* License terms: GNU General Public License (GPL) version 2
* ST DDC I2C master mode driver, used in e.g. U300 series platforms.
* Author: Linus Walleij <linus.walleij@stericsson.com>
@@ -139,8 +139,6 @@ module_param(scl_frequency, uint, 0644);
* struct stu300_dev - the stu300 driver state holder
* @pdev: parent platform device
* @adapter: corresponding I2C adapter
- * @phybase: location of I/O area in memory
- * @physize: size of I/O area in memory
* @clk: hardware block clock
* @irq: assigned interrupt line
* @cmd_issue_lock: this locks the following cmd_ variables
@@ -155,8 +153,6 @@ module_param(scl_frequency, uint, 0644);
struct stu300_dev {
struct platform_device *pdev;
struct i2c_adapter adapter;
- resource_size_t phybase;
- resource_size_t physize;
void __iomem *virtbase;
struct clk *clk;
int irq;
@@ -873,64 +869,44 @@ stu300_probe(struct platform_device *pdev)
int ret = 0;
char clk_name[] = "I2C0";
- dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL);
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct stu300_dev), GFP_KERNEL);
if (!dev) {
dev_err(&pdev->dev, "could not allocate device struct\n");
- ret = -ENOMEM;
- goto err_no_devmem;
+ return -ENOMEM;
}
bus_nr = pdev->id;
clk_name[3] += (char)bus_nr;
- dev->clk = clk_get(&pdev->dev, clk_name);
+ dev->clk = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(dev->clk)) {
- ret = PTR_ERR(dev->clk);
dev_err(&pdev->dev, "could not retrieve i2c bus clock\n");
- goto err_no_clk;
+ return PTR_ERR(dev->clk);
}
dev->pdev = pdev;
- platform_set_drvdata(pdev, dev);
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto err_no_resource;
- }
-
- dev->phybase = res->start;
- dev->physize = resource_size(res);
-
- if (request_mem_region(dev->phybase, dev->physize,
- NAME " I/O Area") == NULL) {
- ret = -EBUSY;
- goto err_no_ioregion;
- }
+ if (!res)
+ return -ENOENT;
- dev->virtbase = ioremap(dev->phybase, dev->physize);
+ dev->virtbase = devm_request_and_ioremap(&pdev->dev, res);
dev_dbg(&pdev->dev, "initialize bus device I2C%d on virtual "
"base %p\n", bus_nr, dev->virtbase);
- if (!dev->virtbase) {
- ret = -ENOMEM;
- goto err_no_ioremap;
- }
+ if (!dev->virtbase)
+ return -ENOMEM;
dev->irq = platform_get_irq(pdev, 0);
- if (request_irq(dev->irq, stu300_irh, 0,
- NAME, dev)) {
- ret = -EIO;
- goto err_no_irq;
- }
+ ret = devm_request_irq(&pdev->dev, dev->irq, stu300_irh, 0, NAME, dev);
+ if (ret < 0)
+ return ret;
dev->speed = scl_frequency;
- clk_enable(dev->clk);
+ clk_prepare_enable(dev->clk);
ret = stu300_init_hw(dev);
clk_disable(dev->clk);
-
if (ret != 0) {
dev_err(&dev->pdev->dev, "error initializing hardware.\n");
- goto err_init_hw;
+ return -EIO;
}
/* IRQ event handling initialization */
@@ -952,57 +928,43 @@ stu300_probe(struct platform_device *pdev)
/* i2c device drivers may be active on return from add_adapter() */
ret = i2c_add_numbered_adapter(adap);
if (ret) {
- dev_err(&dev->pdev->dev, "failure adding ST Micro DDC "
+ dev_err(&pdev->dev, "failure adding ST Micro DDC "
"I2C adapter\n");
- goto err_add_adapter;
+ return ret;
}
- return 0;
- err_add_adapter:
- err_init_hw:
- free_irq(dev->irq, dev);
- err_no_irq:
- iounmap(dev->virtbase);
- err_no_ioremap:
- release_mem_region(dev->phybase, dev->physize);
- err_no_ioregion:
- platform_set_drvdata(pdev, NULL);
- err_no_resource:
- clk_put(dev->clk);
- err_no_clk:
- kfree(dev);
- err_no_devmem:
- dev_err(&pdev->dev, "failed to add " NAME " adapter: %d\n",
- pdev->id);
- return ret;
+ platform_set_drvdata(pdev, dev);
+ return 0;
}
#ifdef CONFIG_PM
-static int stu300_suspend(struct platform_device *pdev, pm_message_t state)
+static int stu300_suspend(struct device *device)
{
- struct stu300_dev *dev = platform_get_drvdata(pdev);
+ struct stu300_dev *dev = dev_get_drvdata(device);
/* Turn off everything */
stu300_wr8(0x00, dev->virtbase + I2C_CR);
return 0;
}
-static int stu300_resume(struct platform_device *pdev)
+static int stu300_resume(struct device *device)
{
int ret = 0;
- struct stu300_dev *dev = platform_get_drvdata(pdev);
+ struct stu300_dev *dev = dev_get_drvdata(device);
clk_enable(dev->clk);
ret = stu300_init_hw(dev);
clk_disable(dev->clk);
if (ret != 0)
- dev_err(&pdev->dev, "error re-initializing hardware.\n");
+ dev_err(device, "error re-initializing hardware.\n");
return ret;
}
+
+static SIMPLE_DEV_PM_OPS(stu300_pm, stu300_suspend, stu300_resume);
+#define STU300_I2C_PM (&stu300_pm)
#else
-#define stu300_suspend NULL
-#define stu300_resume NULL
+#define STU300_I2C_PM NULL
#endif
static int __exit
@@ -1013,12 +975,7 @@ stu300_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
/* Turn off everything */
stu300_wr8(0x00, dev->virtbase + I2C_CR);
- free_irq(dev->irq, dev);
- iounmap(dev->virtbase);
- release_mem_region(dev->phybase, dev->physize);
- clk_put(dev->clk);
platform_set_drvdata(pdev, NULL);
- kfree(dev);
return 0;
}
@@ -1026,10 +983,9 @@ static struct platform_driver stu300_i2c_driver = {
.driver = {
.name = NAME,
.owner = THIS_MODULE,
+ .pm = STU300_I2C_PM,
},
.remove = __exit_p(stu300_remove),
- .suspend = stu300_suspend,
- .resume = stu300_resume,
};
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 3da7ee3eb505..66eb53fac202 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -97,8 +97,21 @@
#define I2C_HEADER_10BIT_ADDR (1<<18)
#define I2C_HEADER_IE_ENABLE (1<<17)
#define I2C_HEADER_REPEAT_START (1<<16)
+#define I2C_HEADER_CONTINUE_XFER (1<<15)
#define I2C_HEADER_MASTER_ADDR_SHIFT 12
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
+/*
+ * msg_end_type: The bus control which need to be send at end of transfer.
+ * @MSG_END_STOP: Send stop pulse at end of transfer.
+ * @MSG_END_REPEAT_START: Send repeat start at end of transfer.
+ * @MSG_END_CONTINUE: The following on message is coming and so do not send
+ * stop or repeat start.
+ */
+enum msg_end_type {
+ MSG_END_STOP,
+ MSG_END_REPEAT_START,
+ MSG_END_CONTINUE,
+};
/**
* struct tegra_i2c_dev - per device i2c context
@@ -106,7 +119,6 @@
* @adapter: core i2c layer adapter information
* @clk: clock reference for i2c controller
* @i2c_clk: clock reference for i2c bus
- * @iomem: memory resource for registers
* @base: ioremapped registers cookie
* @cont_id: i2c controller id, used for for packet header
* @irq: irq number of transfer complete interrupt
@@ -124,7 +136,6 @@ struct tegra_i2c_dev {
struct i2c_adapter adapter;
struct clk *clk;
struct clk *i2c_clk;
- struct resource *iomem;
void __iomem *base;
int cont_id;
int irq;
@@ -165,6 +176,10 @@ static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
unsigned long reg)
{
writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+
+ /* Read back register to make sure that register writes completed */
+ if (reg != I2C_TX_FIFO)
+ readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
}
static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
@@ -449,7 +464,7 @@ err:
}
static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
- struct i2c_msg *msg, int stop)
+ struct i2c_msg *msg, enum msg_end_type end_state)
{
u32 packet_header;
u32 int_mask;
@@ -476,7 +491,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
packet_header = I2C_HEADER_IE_ENABLE;
- if (!stop)
+ if (end_state == MSG_END_CONTINUE)
+ packet_header |= I2C_HEADER_CONTINUE_XFER;
+ else if (end_state == MSG_END_REPEAT_START)
packet_header |= I2C_HEADER_REPEAT_START;
if (msg->flags & I2C_M_TEN) {
packet_header |= msg->addr;
@@ -548,8 +565,14 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
clk_prepare_enable(i2c_dev->clk);
for (i = 0; i < num; i++) {
- int stop = (i == (num - 1)) ? 1 : 0;
- ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop);
+ enum msg_end_type end_type = MSG_END_STOP;
+ if (i < (num - 1)) {
+ if (msgs[i + 1].flags & I2C_M_NOSTART)
+ end_type = MSG_END_CONTINUE;
+ else
+ end_type = MSG_END_REPEAT_START;
+ }
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
}
@@ -559,7 +582,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
}
static const struct i2c_algorithm tegra_i2c_algo = {
@@ -572,7 +596,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
struct tegra_i2c_dev *i2c_dev;
struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
- struct resource *iomem;
struct clk *clk;
struct clk *i2c_clk;
const unsigned int *prop;
@@ -585,50 +608,41 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "no mem resource\n");
return -EINVAL;
}
- iomem = request_mem_region(res->start, resource_size(res), pdev->name);
- if (!iomem) {
- dev_err(&pdev->dev, "I2C region already claimed\n");
- return -EBUSY;
- }
- base = ioremap(iomem->start, resource_size(iomem));
+ base = devm_request_and_ioremap(&pdev->dev, res);
if (!base) {
- dev_err(&pdev->dev, "Cannot ioremap I2C region\n");
- return -ENOMEM;
+ dev_err(&pdev->dev, "Cannot request/ioremap I2C registers\n");
+ return -EADDRNOTAVAIL;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "no irq resource\n");
- ret = -EINVAL;
- goto err_iounmap;
+ return -EINVAL;
}
irq = res->start;
- clk = clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "missing controller clock");
- ret = PTR_ERR(clk);
- goto err_release_region;
+ return PTR_ERR(clk);
}
- i2c_clk = clk_get(&pdev->dev, "i2c");
+ i2c_clk = devm_clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c_clk)) {
dev_err(&pdev->dev, "missing bus clock");
- ret = PTR_ERR(i2c_clk);
- goto err_clk_put;
+ return PTR_ERR(i2c_clk);
}
- i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev) {
- ret = -ENOMEM;
- goto err_i2c_clk_put;
+ dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev");
+ return -ENOMEM;
}
i2c_dev->base = base;
i2c_dev->clk = clk;
i2c_dev->i2c_clk = i2c_clk;
- i2c_dev->iomem = iomem;
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
@@ -657,13 +671,14 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
ret = tegra_i2c_init(i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize i2c controller");
- goto err_free;
+ return ret;
}
- ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev);
+ ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
+ tegra_i2c_isr, 0, pdev->name, i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
- goto err_free;
+ return ret;
}
clk_prepare_enable(i2c_dev->i2c_clk);
@@ -681,45 +696,26 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
- goto err_free_irq;
+ clk_disable_unprepare(i2c_dev->i2c_clk);
+ return ret;
}
of_i2c_register_devices(&i2c_dev->adapter);
return 0;
-err_free_irq:
- free_irq(i2c_dev->irq, i2c_dev);
-err_free:
- kfree(i2c_dev);
-err_i2c_clk_put:
- clk_put(i2c_clk);
-err_clk_put:
- clk_put(clk);
-err_release_region:
- release_mem_region(iomem->start, resource_size(iomem));
-err_iounmap:
- iounmap(base);
- return ret;
}
static int __devexit tegra_i2c_remove(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c_dev->adapter);
- free_irq(i2c_dev->irq, i2c_dev);
- clk_put(i2c_dev->i2c_clk);
- clk_put(i2c_dev->clk);
- release_mem_region(i2c_dev->iomem->start,
- resource_size(i2c_dev->iomem));
- iounmap(i2c_dev->base);
- kfree(i2c_dev);
return 0;
}
#ifdef CONFIG_PM
-static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+static int tegra_i2c_suspend(struct device *dev)
{
- struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
i2c_lock_adapter(&i2c_dev->adapter);
i2c_dev->is_suspended = true;
@@ -728,9 +724,9 @@ static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int tegra_i2c_resume(struct platform_device *pdev)
+static int tegra_i2c_resume(struct device *dev)
{
- struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+ struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
int ret;
i2c_lock_adapter(&i2c_dev->adapter);
@@ -748,6 +744,11 @@ static int tegra_i2c_resume(struct platform_device *pdev)
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
+#define TEGRA_I2C_PM (&tegra_i2c_pm)
+#else
+#define TEGRA_I2C_PM NULL
#endif
#if defined(CONFIG_OF)
@@ -758,21 +759,16 @@ static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
{},
};
MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
-#else
-#define tegra_i2c_of_match NULL
#endif
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = __devexit_p(tegra_i2c_remove),
-#ifdef CONFIG_PM
- .suspend = tegra_i2c_suspend,
- .resume = tegra_i2c_resume,
-#endif
.driver = {
.name = "tegra-i2c",
.owner = THIS_MODULE,
- .of_match_table = tegra_i2c_of_match,
+ .of_match_table = of_match_ptr(tegra_i2c_of_match),
+ .pm = TEGRA_I2C_PM,
},
};
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 26488aa893d5..2efa56c5ff2c 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1312,6 +1312,37 @@ module_exit(i2c_exit);
*/
/**
+ * __i2c_transfer - unlocked flavor of i2c_transfer
+ * @adap: Handle to I2C bus
+ * @msgs: One or more messages to execute before STOP is issued to
+ * terminate the operation; each message begins with a START.
+ * @num: Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ *
+ * Adapter lock must be held when calling this function. No debug logging
+ * takes place. adap->algo->master_xfer existence isn't checked.
+ */
+int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ unsigned long orig_jiffies;
+ int ret, try;
+
+ /* Retry automatically on arbitration loss */
+ orig_jiffies = jiffies;
+ for (ret = 0, try = 0; try <= adap->retries; try++) {
+ ret = adap->algo->master_xfer(adap, msgs, num);
+ if (ret != -EAGAIN)
+ break;
+ if (time_after(jiffies, orig_jiffies + adap->timeout))
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(__i2c_transfer);
+
+/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
@@ -1325,8 +1356,7 @@ module_exit(i2c_exit);
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
- unsigned long orig_jiffies;
- int ret, try;
+ int ret;
/* REVISIT the fault reporting model here is weak:
*
@@ -1364,15 +1394,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
i2c_lock_adapter(adap);
}
- /* Retry automatically on arbitration loss */
- orig_jiffies = jiffies;
- for (ret = 0, try = 0; try <= adap->retries; try++) {
- ret = adap->algo->master_xfer(adap, msgs, num);
- if (ret != -EAGAIN)
- break;
- if (time_after(jiffies, orig_jiffies + adap->timeout))
- break;
- }
+ ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_adapter(adap);
return ret;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 5a335b5447c6..7172559ce0c1 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -3064,10 +3064,7 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
id_priv->id.port_num, &rec,
comp_mask, GFP_KERNEL,
cma_ib_mc_handler, mc);
- if (IS_ERR(mc->multicast.ib))
- return PTR_ERR(mc->multicast.ib);
-
- return 0;
+ return PTR_RET(mc->multicast.ib);
}
static void iboe_mcast_work_handler(struct work_struct *work)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 893cb879462c..6bf850422895 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1002,23 +1002,18 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- optval = kmalloc(cmd.optlen, GFP_KERNEL);
- if (!optval) {
- ret = -ENOMEM;
- goto out1;
- }
-
- if (copy_from_user(optval, (void __user *) (unsigned long) cmd.optval,
- cmd.optlen)) {
- ret = -EFAULT;
- goto out2;
+ optval = memdup_user((void __user *) (unsigned long) cmd.optval,
+ cmd.optlen);
+ if (IS_ERR(optval)) {
+ ret = PTR_ERR(optval);
+ goto out;
}
ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval,
cmd.optlen);
-out2:
kfree(optval);
-out1:
+
+out:
ucma_put_ctx(ctx);
return ret;
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index b2f9784beb4a..cb5b7f7d4d38 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -893,7 +893,9 @@ static int ocrdma_check_qp_params(struct ib_pd *ibpd, struct ocrdma_dev *dev,
/* verify consumer QPs are not trying to use GSI QP's CQ */
if ((attrs->qp_type != IB_QPT_GSI) && (dev->gsi_qp_created)) {
if ((dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq)) ||
- (dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq))) {
+ (dev->gsi_sqcq == get_ocrdma_cq(attrs->recv_cq)) ||
+ (dev->gsi_rqcq == get_ocrdma_cq(attrs->send_cq)) ||
+ (dev->gsi_rqcq == get_ocrdma_cq(attrs->recv_cq))) {
ocrdma_err("%s(%d) Consumer QP cannot use GSI CQs.\n",
__func__, dev->id);
return -EINVAL;
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 6e19ec844d99..7b1b86690024 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -656,6 +656,11 @@ struct qib_pportdata {
/* 16 congestion entries with each entry corresponding to a SL */
struct ib_cc_congestion_entry_shadow *congestion_entries;
+ /* Maximum number of congestion control entries that the agent expects
+ * the manager to send.
+ */
+ u16 cc_supported_table_entries;
+
/* Total number of congestion control table entries */
u16 total_cct_entry;
@@ -667,11 +672,6 @@ struct qib_pportdata {
/* CA's max number of 64 entry units in the congestion control table */
u8 cc_max_table_entries;
-
- /* Maximum number of congestion control entries that the agent expects
- * the manager to send.
- */
- u8 cc_supported_table_entries;
};
/* Observers. Not to be taken lightly, possibly not to ship. */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 86df632ea612..ca43901ed861 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -92,6 +92,8 @@ enum {
IPOIB_STOP_REAPER = 7,
IPOIB_FLAG_ADMIN_CM = 9,
IPOIB_FLAG_UMCAST = 10,
+ IPOIB_STOP_NEIGH_GC = 11,
+ IPOIB_NEIGH_TBL_FLUSH = 12,
IPOIB_MAX_BACKOFF_SECONDS = 16,
@@ -260,6 +262,20 @@ struct ipoib_ethtool_st {
u16 max_coalesced_frames;
};
+struct ipoib_neigh_hash {
+ struct ipoib_neigh __rcu **buckets;
+ struct rcu_head rcu;
+ u32 mask;
+ u32 size;
+};
+
+struct ipoib_neigh_table {
+ struct ipoib_neigh_hash __rcu *htbl;
+ rwlock_t rwlock;
+ atomic_t entries;
+ struct completion flushed;
+};
+
/*
* Device private locking: network stack tx_lock protects members used
* in TX fast path, lock protects everything else. lock nests inside
@@ -279,6 +295,8 @@ struct ipoib_dev_priv {
struct rb_root path_tree;
struct list_head path_list;
+ struct ipoib_neigh_table ntbl;
+
struct ipoib_mcast *broadcast;
struct list_head multicast_list;
struct rb_root multicast_tree;
@@ -291,7 +309,7 @@ struct ipoib_dev_priv {
struct work_struct flush_heavy;
struct work_struct restart_task;
struct delayed_work ah_reap_task;
-
+ struct delayed_work neigh_reap_task;
struct ib_device *ca;
u8 port;
u16 pkey;
@@ -377,13 +395,16 @@ struct ipoib_neigh {
#ifdef CONFIG_INFINIBAND_IPOIB_CM
struct ipoib_cm_tx *cm;
#endif
- union ib_gid dgid;
+ u8 daddr[INFINIBAND_ALEN];
struct sk_buff_head queue;
- struct neighbour *neighbour;
struct net_device *dev;
struct list_head list;
+ struct ipoib_neigh __rcu *hnext;
+ struct rcu_head rcu;
+ atomic_t refcnt;
+ unsigned long alive;
};
#define IPOIB_UD_MTU(ib_mtu) (ib_mtu - IPOIB_ENCAP_LEN)
@@ -394,21 +415,17 @@ static inline int ipoib_ud_need_sg(unsigned int ib_mtu)
return IPOIB_UD_BUF_SIZE(ib_mtu) > PAGE_SIZE;
}
-/*
- * We stash a pointer to our private neighbour information after our
- * hardware address in neigh->ha. The ALIGN() expression here makes
- * sure that this pointer is stored aligned so that an unaligned
- * load is not needed to dereference it.
- */
-static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh);
+static inline void ipoib_neigh_put(struct ipoib_neigh *neigh)
{
- return (void*) neigh + ALIGN(offsetof(struct neighbour, ha) +
- INFINIBAND_ALEN, sizeof(void *));
+ if (atomic_dec_and_test(&neigh->refcnt))
+ ipoib_neigh_dtor(neigh);
}
-
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh,
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr);
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
struct net_device *dev);
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
+void ipoib_neigh_free(struct ipoib_neigh *neigh);
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid);
extern struct workqueue_struct *ipoib_workqueue;
@@ -425,7 +442,6 @@ static inline void ipoib_put_ah(struct ipoib_ah *ah)
{
kref_put(&ah->ref, ipoib_free_ah);
}
-
int ipoib_open(struct net_device *dev);
int ipoib_add_pkey_attr(struct net_device *dev);
int ipoib_add_umcast_attr(struct net_device *dev);
@@ -455,7 +471,7 @@ void ipoib_dev_cleanup(struct net_device *dev);
void ipoib_mcast_join_task(struct work_struct *work);
void ipoib_mcast_carrier_on_task(struct work_struct *work);
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb);
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb);
void ipoib_mcast_restart_task(struct work_struct *work);
int ipoib_mcast_start_thread(struct net_device *dev);
@@ -517,10 +533,10 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
}
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- return IPOIB_CM_SUPPORTED(n->ha) &&
+ return IPOIB_CM_SUPPORTED(hwaddr) &&
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
}
@@ -575,7 +591,7 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
{
return 0;
}
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
{
return 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 6d66ab0dd92a..95ecf4eadf5f 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -811,9 +811,7 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
tx->neigh = NULL;
}
@@ -1230,9 +1228,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
tx->neigh = NULL;
}
@@ -1279,7 +1275,7 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
list_move(&tx->list, &priv->cm.reap_list);
queue_work(ipoib_workqueue, &priv->cm.reap_task);
ipoib_dbg(priv, "Reap connection for gid %pI6\n",
- tx->neigh->dgid.raw);
+ tx->neigh->daddr + 4);
tx->neigh = NULL;
}
}
@@ -1304,7 +1300,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
p = list_entry(priv->cm.start_list.next, typeof(*p), list);
list_del_init(&p->list);
neigh = p->neigh;
- qpn = IPOIB_QPN(neigh->neighbour->ha);
+ qpn = IPOIB_QPN(neigh->daddr);
memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1320,9 +1316,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
}
list_del(&p->list);
kfree(p);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index bbee4b2d7a13..97920b77a5d0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -46,7 +46,8 @@
#include <linux/ip.h>
#include <linux/in.h>
-#include <net/dst.h>
+#include <linux/jhash.h>
+#include <net/arp.h>
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
@@ -84,6 +85,7 @@ struct ib_sa_client ipoib_sa_client;
static void ipoib_add_one(struct ib_device *device);
static void ipoib_remove_one(struct ib_device *device);
+static void ipoib_neigh_reclaim(struct rcu_head *rp);
static struct ib_client ipoib_client = {
.name = "ipoib",
@@ -264,30 +266,15 @@ static int __path_add(struct net_device *dev, struct ipoib_path *path)
static void path_free(struct net_device *dev, struct ipoib_path *path)
{
- struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct ipoib_neigh *neigh, *tn;
struct sk_buff *skb;
- unsigned long flags;
while ((skb = __skb_dequeue(&path->queue)))
dev_kfree_skb_irq(skb);
- spin_lock_irqsave(&priv->lock, flags);
-
- list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) {
- /*
- * It's safe to call ipoib_put_ah() inside priv->lock
- * here, because we know that path->ah will always
- * hold one more reference, so ipoib_put_ah() will
- * never do more than decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
-
- ipoib_neigh_free(dev, neigh);
- }
+ ipoib_dbg(netdev_priv(dev), "path_free\n");
- spin_unlock_irqrestore(&priv->lock, flags);
+ /* remove all neigh connected to this path */
+ ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw);
if (path->ah)
ipoib_put_ah(path->ah);
@@ -458,19 +445,15 @@ static void path_rec_completion(int status,
}
kref_get(&path->ah->ref);
neigh->ah = path->ah;
- memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
- sizeof(union ib_gid));
- if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+ if (ipoib_cm_enabled(dev, neigh->daddr)) {
if (!ipoib_cm_get(neigh))
ipoib_cm_set(neigh, ipoib_cm_create_tx(dev,
path,
neigh));
if (!ipoib_cm_get(neigh)) {
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
continue;
}
}
@@ -555,15 +538,15 @@ static int path_rec_start(struct net_device *dev,
return 0;
}
-/* called with rcu_read_lock */
-static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
+static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
+ struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
struct ipoib_neigh *neigh;
unsigned long flags;
- neigh = ipoib_neigh_alloc(n, skb->dev);
+ neigh = ipoib_neigh_alloc(daddr, dev);
if (!neigh) {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
@@ -572,9 +555,9 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
spin_lock_irqsave(&priv->lock, flags);
- path = __path_find(dev, n->ha + 4);
+ path = __path_find(dev, daddr + 4);
if (!path) {
- path = path_rec_create(dev, n->ha + 4);
+ path = path_rec_create(dev, daddr + 4);
if (!path)
goto err_path;
@@ -586,17 +569,13 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
if (path->ah) {
kref_get(&path->ah->ref);
neigh->ah = path->ah;
- memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
- sizeof(union ib_gid));
- if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+ if (ipoib_cm_enabled(dev, neigh->daddr)) {
if (!ipoib_cm_get(neigh))
ipoib_cm_set(neigh, ipoib_cm_create_tx(dev, path, neigh));
if (!ipoib_cm_get(neigh)) {
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
goto err_drop;
}
if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
@@ -608,7 +587,8 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
}
} else {
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr));
+ ipoib_neigh_put(neigh);
return;
}
} else {
@@ -621,35 +601,20 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
}
spin_unlock_irqrestore(&priv->lock, flags);
+ ipoib_neigh_put(neigh);
return;
err_list:
list_del(&neigh->list);
err_path:
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
err_drop:
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-/* called with rcu_read_lock */
-static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
-{
- struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
-
- /* Look up path record for unicasts */
- if (n->ha[4] != 0xff) {
- neigh_add_path(skb, n, dev);
- return;
- }
-
- /* Add in the P_Key for multicasts */
- n->ha[8] = (priv->pkey >> 8) & 0xff;
- n->ha[9] = priv->pkey & 0xff;
- ipoib_mcast_send(dev, n->ha + 4, skb);
+ ipoib_neigh_put(neigh);
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -710,96 +675,80 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct neighbour *n = NULL;
+ struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
+ struct ipoib_header *header;
unsigned long flags;
- rcu_read_lock();
- if (likely(skb_dst(skb))) {
- n = dst_neigh_lookup_skb(skb_dst(skb), skb);
- if (!n) {
+ header = (struct ipoib_header *) skb->data;
+
+ if (unlikely(cb->hwaddr[4] == 0xff)) {
+ /* multicast, arrange "if" according to probability */
+ if ((header->proto != htons(ETH_P_IP)) &&
+ (header->proto != htons(ETH_P_IPV6)) &&
+ (header->proto != htons(ETH_P_ARP)) &&
+ (header->proto != htons(ETH_P_RARP))) {
+ /* ethertype not supported by IPoIB */
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
- goto unlock;
+ return NETDEV_TX_OK;
}
+ /* Add in the P_Key for multicast*/
+ cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+ cb->hwaddr[9] = priv->pkey & 0xff;
+
+ neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ if (likely(neigh))
+ goto send_using_neigh;
+ ipoib_mcast_send(dev, cb->hwaddr, skb);
+ return NETDEV_TX_OK;
}
- if (likely(n)) {
- if (unlikely(!*to_ipoib_neigh(n))) {
- ipoib_path_lookup(skb, n, dev);
- goto unlock;
- }
-
- neigh = *to_ipoib_neigh(n);
- if (unlikely((memcmp(&neigh->dgid.raw,
- n->ha + 4,
- sizeof(union ib_gid))) ||
- (neigh->dev != dev))) {
- spin_lock_irqsave(&priv->lock, flags);
- /*
- * It's safe to call ipoib_put_ah() inside
- * priv->lock here, because we know that
- * path->ah will always hold one more reference,
- * so ipoib_put_ah() will never do more than
- * decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- list_del(&neigh->list);
- ipoib_neigh_free(dev, neigh);
- spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_path_lookup(skb, n, dev);
- goto unlock;
+ /* unicast, arrange "switch" according to probability */
+ switch (header->proto) {
+ case htons(ETH_P_IP):
+ case htons(ETH_P_IPV6):
+ neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ if (unlikely(!neigh)) {
+ neigh_add_path(skb, cb->hwaddr, dev);
+ return NETDEV_TX_OK;
}
+ break;
+ case htons(ETH_P_ARP):
+ case htons(ETH_P_RARP):
+ /* for unicast ARP and RARP should always perform path find */
+ unicast_arp_send(skb, dev, cb);
+ return NETDEV_TX_OK;
+ default:
+ /* ethertype not supported by IPoIB */
+ ++dev->stats.tx_dropped;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
- if (ipoib_cm_get(neigh)) {
- if (ipoib_cm_up(neigh)) {
- ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
- goto unlock;
- }
- } else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
- goto unlock;
+send_using_neigh:
+ /* note we now hold a ref to neigh */
+ if (ipoib_cm_get(neigh)) {
+ if (ipoib_cm_up(neigh)) {
+ ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
+ goto unref;
}
+ } else if (neigh->ah) {
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+ goto unref;
+ }
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
- spin_lock_irqsave(&priv->lock, flags);
- __skb_queue_tail(&neigh->queue, skb);
- spin_unlock_irqrestore(&priv->lock, flags);
- } else {
- ++dev->stats.tx_dropped;
- dev_kfree_skb_any(skb);
- }
+ if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ spin_lock_irqsave(&priv->lock, flags);
+ __skb_queue_tail(&neigh->queue, skb);
+ spin_unlock_irqrestore(&priv->lock, flags);
} else {
- struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
-
- if (cb->hwaddr[4] == 0xff) {
- /* Add in the P_Key for multicast*/
- cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
- cb->hwaddr[9] = priv->pkey & 0xff;
+ ++dev->stats.tx_dropped;
+ dev_kfree_skb_any(skb);
+ }
- ipoib_mcast_send(dev, cb->hwaddr + 4, skb);
- } else {
- /* unicast GID -- should be ARP or RARP reply */
-
- if ((be16_to_cpup((__be16 *) skb->data) != ETH_P_ARP) &&
- (be16_to_cpup((__be16 *) skb->data) != ETH_P_RARP)) {
- ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n",
- skb_dst(skb) ? "neigh" : "dst",
- be16_to_cpup((__be16 *) skb->data),
- IPOIB_QPN(cb->hwaddr),
- cb->hwaddr + 4);
- dev_kfree_skb_any(skb);
- ++dev->stats.tx_dropped;
- goto unlock;
- }
+unref:
+ ipoib_neigh_put(neigh);
- unicast_arp_send(skb, dev, cb);
- }
- }
-unlock:
- if (n)
- neigh_release(n);
- rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -821,6 +770,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
const void *daddr, const void *saddr, unsigned len)
{
struct ipoib_header *header;
+ struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -828,14 +778,11 @@ static int ipoib_hard_header(struct sk_buff *skb,
header->reserved = 0;
/*
- * If we don't have a dst_entry structure, stuff the
+ * we don't rely on dst_entry structure, always stuff the
* destination address into skb->cb so we can figure out where
* to send the packet later.
*/
- if (!skb_dst(skb)) {
- struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
- memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
- }
+ memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
return 0;
}
@@ -852,86 +799,438 @@ static void ipoib_set_mcast_list(struct net_device *dev)
queue_work(ipoib_workqueue, &priv->restart_task);
}
-static void ipoib_neigh_cleanup(struct neighbour *n)
+static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
{
- struct ipoib_neigh *neigh;
- struct ipoib_dev_priv *priv = netdev_priv(n->dev);
+ /*
+ * Use only the address parts that contributes to spreading
+ * The subnet prefix is not used as one can not connect to
+ * same remote port (GUID) using the same remote QPN via two
+ * different subnets.
+ */
+ /* qpn octets[1:4) & port GUID octets[12:20) */
+ u32 *daddr_32 = (u32 *) daddr;
+ u32 hv;
+
+ hv = jhash_3words(daddr_32[3], daddr_32[4], 0xFFFFFF & daddr_32[0], 0);
+ return hv & htbl->mask;
+}
+
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh *neigh = NULL;
+ u32 hash_val;
+
+ rcu_read_lock_bh();
+
+ htbl = rcu_dereference_bh(ntbl->htbl);
+
+ if (!htbl)
+ goto out_unlock;
+
+ hash_val = ipoib_addr_hash(htbl, daddr);
+ for (neigh = rcu_dereference_bh(htbl->buckets[hash_val]);
+ neigh != NULL;
+ neigh = rcu_dereference_bh(neigh->hnext)) {
+ if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+ /* found, take one ref on behalf of the caller */
+ if (!atomic_inc_not_zero(&neigh->refcnt)) {
+ /* deleted */
+ neigh = NULL;
+ goto out_unlock;
+ }
+ neigh->alive = jiffies;
+ goto out_unlock;
+ }
+ }
+
+out_unlock:
+ rcu_read_unlock_bh();
+ return neigh;
+}
+
+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
+{
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long neigh_obsolete;
+ unsigned long dt;
unsigned long flags;
- struct ipoib_ah *ah = NULL;
+ int i;
- neigh = *to_ipoib_neigh(n);
- if (neigh)
- priv = netdev_priv(neigh->dev);
- else
+ if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
return;
- ipoib_dbg(priv,
- "neigh_cleanup for %06x %pI6\n",
- IPOIB_QPN(n->ha),
- n->ha + 4);
- spin_lock_irqsave(&priv->lock, flags);
+ write_lock_bh(&ntbl->rwlock);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&ntbl->rwlock));
+
+ if (!htbl)
+ goto out_unlock;
+
+ /* neigh is obsolete if it was idle for two GC periods */
+ dt = 2 * arp_tbl.gc_interval;
+ neigh_obsolete = jiffies - dt;
+ /* handle possible race condition */
+ if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+ goto out_unlock;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ /* was the neigh idle for two GC periods */
+ if (time_after(neigh_obsolete, neigh->alive)) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock)));
+ /* remove from path/mc list */
+ spin_lock_irqsave(&priv->lock, flags);
+ list_del(&neigh->list);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ } else {
+ np = &neigh->hnext;
+ }
- if (neigh->ah)
- ah = neigh->ah;
- list_del(&neigh->list);
- ipoib_neigh_free(n->dev, neigh);
+ }
+ }
- spin_unlock_irqrestore(&priv->lock, flags);
+out_unlock:
+ write_unlock_bh(&ntbl->rwlock);
+}
- if (ah)
- ipoib_put_ah(ah);
+static void ipoib_reap_neigh(struct work_struct *work)
+{
+ struct ipoib_dev_priv *priv =
+ container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
+
+ __ipoib_reap_neigh(priv);
+
+ if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+ queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+ arp_tbl.gc_interval);
}
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour,
+
+static struct ipoib_neigh *ipoib_neigh_ctor(u8 *daddr,
struct net_device *dev)
{
struct ipoib_neigh *neigh;
- neigh = kmalloc(sizeof *neigh, GFP_ATOMIC);
+ neigh = kzalloc(sizeof *neigh, GFP_ATOMIC);
if (!neigh)
return NULL;
- neigh->neighbour = neighbour;
neigh->dev = dev;
- memset(&neigh->dgid.raw, 0, sizeof (union ib_gid));
- *to_ipoib_neigh(neighbour) = neigh;
+ memcpy(&neigh->daddr, daddr, sizeof(neigh->daddr));
skb_queue_head_init(&neigh->queue);
+ INIT_LIST_HEAD(&neigh->list);
ipoib_cm_set(neigh, NULL);
+ /* one ref on behalf of the caller */
+ atomic_set(&neigh->refcnt, 1);
+
+ return neigh;
+}
+
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
+ struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh *neigh;
+ u32 hash_val;
+
+ write_lock_bh(&ntbl->rwlock);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&ntbl->rwlock));
+ if (!htbl) {
+ neigh = NULL;
+ goto out_unlock;
+ }
+
+ /* need to add a new neigh, but maybe some other thread succeeded?
+ * recalc hash, maybe hash resize took place so we do a search
+ */
+ hash_val = ipoib_addr_hash(htbl, daddr);
+ for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
+ lockdep_is_held(&ntbl->rwlock));
+ neigh != NULL;
+ neigh = rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock))) {
+ if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+ /* found, take one ref on behalf of the caller */
+ if (!atomic_inc_not_zero(&neigh->refcnt)) {
+ /* deleted */
+ neigh = NULL;
+ break;
+ }
+ neigh->alive = jiffies;
+ goto out_unlock;
+ }
+ }
+
+ neigh = ipoib_neigh_ctor(daddr, dev);
+ if (!neigh)
+ goto out_unlock;
+
+ /* one ref on behalf of the hash table */
+ atomic_inc(&neigh->refcnt);
+ neigh->alive = jiffies;
+ /* put in hash */
+ rcu_assign_pointer(neigh->hnext,
+ rcu_dereference_protected(htbl->buckets[hash_val],
+ lockdep_is_held(&ntbl->rwlock)));
+ rcu_assign_pointer(htbl->buckets[hash_val], neigh);
+ atomic_inc(&ntbl->entries);
+
+out_unlock:
+ write_unlock_bh(&ntbl->rwlock);
return neigh;
}
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh)
{
+ /* neigh reference count was dropprd to zero */
+ struct net_device *dev = neigh->dev;
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
- *to_ipoib_neigh(neigh->neighbour) = NULL;
+ if (neigh->ah)
+ ipoib_put_ah(neigh->ah);
while ((skb = __skb_dequeue(&neigh->queue))) {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
}
if (ipoib_cm_get(neigh))
ipoib_cm_destroy_tx(ipoib_cm_get(neigh));
+ ipoib_dbg(netdev_priv(dev),
+ "neigh free for %06x %pI6\n",
+ IPOIB_QPN(neigh->daddr),
+ neigh->daddr + 4);
kfree(neigh);
+ if (atomic_dec_and_test(&priv->ntbl.entries)) {
+ if (test_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags))
+ complete(&priv->ntbl.flushed);
+ }
+}
+
+static void ipoib_neigh_reclaim(struct rcu_head *rp)
+{
+ /* Called as a result of removal from hash table */
+ struct ipoib_neigh *neigh = container_of(rp, struct ipoib_neigh, rcu);
+ /* note TX context may hold another ref */
+ ipoib_neigh_put(neigh);
}
-static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms)
+void ipoib_neigh_free(struct ipoib_neigh *neigh)
{
- parms->neigh_cleanup = ipoib_neigh_cleanup;
+ struct net_device *dev = neigh->dev;
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh __rcu **np;
+ struct ipoib_neigh *n;
+ u32 hash_val;
+
+ write_lock_bh(&ntbl->rwlock);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&ntbl->rwlock));
+ if (!htbl)
+ goto out_unlock;
+
+ hash_val = ipoib_addr_hash(htbl, neigh->daddr);
+ np = &htbl->buckets[hash_val];
+ for (n = rcu_dereference_protected(*np,
+ lockdep_is_held(&ntbl->rwlock));
+ n != NULL;
+ n = rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock))) {
+ if (n == neigh) {
+ /* found */
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock)));
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ goto out_unlock;
+ } else {
+ np = &n->hnext;
+ }
+ }
+
+out_unlock:
+ write_unlock_bh(&ntbl->rwlock);
+
+}
+
+static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
+{
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh **buckets;
+ u32 size;
+
+ clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+ ntbl->htbl = NULL;
+ rwlock_init(&ntbl->rwlock);
+ htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
+ if (!htbl)
+ return -ENOMEM;
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ size = roundup_pow_of_two(arp_tbl.gc_thresh3);
+ buckets = kzalloc(size * sizeof(*buckets), GFP_KERNEL);
+ if (!buckets) {
+ kfree(htbl);
+ return -ENOMEM;
+ }
+ htbl->size = size;
+ htbl->mask = (size - 1);
+ htbl->buckets = buckets;
+ ntbl->htbl = htbl;
+ atomic_set(&ntbl->entries, 0);
+
+ /* start garbage collection */
+ clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+ arp_tbl.gc_interval);
return 0;
}
+static void neigh_hash_free_rcu(struct rcu_head *head)
+{
+ struct ipoib_neigh_hash *htbl = container_of(head,
+ struct ipoib_neigh_hash,
+ rcu);
+ struct ipoib_neigh __rcu **buckets = htbl->buckets;
+
+ kfree(buckets);
+ kfree(htbl);
+}
+
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long flags;
+ int i;
+
+ /* remove all neigh connected to a given path or mcast */
+ write_lock_bh(&ntbl->rwlock);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&ntbl->rwlock));
+
+ if (!htbl)
+ goto out_unlock;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ /* delete neighs belong to this parent */
+ if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock)));
+ /* remove from parent list */
+ spin_lock_irqsave(&priv->lock, flags);
+ list_del(&neigh->list);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ } else {
+ np = &neigh->hnext;
+ }
+
+ }
+ }
+out_unlock:
+ write_unlock_bh(&ntbl->rwlock);
+}
+
+static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
+{
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long flags;
+ int i;
+
+ write_lock_bh(&ntbl->rwlock);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&ntbl->rwlock));
+ if (!htbl)
+ goto out_unlock;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&ntbl->rwlock)));
+ /* remove from path/mc list */
+ spin_lock_irqsave(&priv->lock, flags);
+ list_del(&neigh->list);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ }
+ }
+
+ rcu_assign_pointer(ntbl->htbl, NULL);
+ call_rcu(&htbl->rcu, neigh_hash_free_rcu);
+
+out_unlock:
+ write_unlock_bh(&ntbl->rwlock);
+}
+
+static void ipoib_neigh_hash_uninit(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ int stopped;
+
+ ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
+ init_completion(&priv->ntbl.flushed);
+ set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+
+ /* Stop GC if called at init fail need to cancel work */
+ stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ if (!stopped)
+ cancel_delayed_work(&priv->neigh_reap_task);
+
+ if (atomic_read(&priv->ntbl.entries)) {
+ ipoib_flush_neighs(priv);
+ wait_for_completion(&priv->ntbl.flushed);
+ }
+}
+
+
int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ if (ipoib_neigh_hash_init(priv) < 0)
+ goto out;
/* Allocate RX/TX "rings" to hold queued skbs */
priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring,
GFP_KERNEL);
if (!priv->rx_ring) {
printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n",
ca->name, ipoib_recvq_size);
- goto out;
+ goto out_neigh_hash_cleanup;
}
priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring);
@@ -954,6 +1253,8 @@ out_tx_ring_cleanup:
out_rx_ring_cleanup:
kfree(priv->rx_ring);
+out_neigh_hash_cleanup:
+ ipoib_neigh_hash_uninit(dev);
out:
return -ENOMEM;
}
@@ -966,6 +1267,9 @@ void ipoib_dev_cleanup(struct net_device *dev)
/* Delete any child interfaces first */
list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
+ /* Stop GC on child */
+ set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags);
+ cancel_delayed_work(&cpriv->neigh_reap_task);
unregister_netdev(cpriv->dev);
ipoib_dev_cleanup(cpriv->dev);
free_netdev(cpriv->dev);
@@ -978,6 +1282,8 @@ void ipoib_dev_cleanup(struct net_device *dev)
priv->rx_ring = NULL;
priv->tx_ring = NULL;
+
+ ipoib_neigh_hash_uninit(dev);
}
static const struct header_ops ipoib_header_ops = {
@@ -992,7 +1298,6 @@ static const struct net_device_ops ipoib_netdev_ops = {
.ndo_start_xmit = ipoib_start_xmit,
.ndo_tx_timeout = ipoib_timeout,
.ndo_set_rx_mode = ipoib_set_mcast_list,
- .ndo_neigh_setup = ipoib_neigh_setup_dev,
};
static void ipoib_setup(struct net_device *dev)
@@ -1041,6 +1346,7 @@ static void ipoib_setup(struct net_device *dev)
INIT_WORK(&priv->flush_heavy, ipoib_ib_dev_flush_heavy);
INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task);
INIT_DELAYED_WORK(&priv->ah_reap_task, ipoib_reap_ah);
+ INIT_DELAYED_WORK(&priv->neigh_reap_task, ipoib_reap_neigh);
}
struct ipoib_dev_priv *ipoib_intf_alloc(const char *name)
@@ -1281,6 +1587,9 @@ sysfs_failed:
register_failed:
ib_unregister_event_handler(&priv->event_handler);
+ /* Stop GC if started before flush */
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(ipoib_workqueue);
event_failed:
@@ -1347,6 +1656,9 @@ static void ipoib_remove_one(struct ib_device *device)
dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
rtnl_unlock();
+ /* Stop GC */
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(ipoib_workqueue);
unregister_netdev(priv->dev);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 7cecb16d3d48..13f4aa7593c8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -69,28 +69,13 @@ struct ipoib_mcast_iter {
static void ipoib_mcast_free(struct ipoib_mcast *mcast)
{
struct net_device *dev = mcast->dev;
- struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct ipoib_neigh *neigh, *tmp;
int tx_dropped = 0;
ipoib_dbg_mcast(netdev_priv(dev), "deleting multicast group %pI6\n",
mcast->mcmember.mgid.raw);
- spin_lock_irq(&priv->lock);
-
- list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) {
- /*
- * It's safe to call ipoib_put_ah() inside priv->lock
- * here, because we know that mcast->ah will always
- * hold one more reference, so ipoib_put_ah() will
- * never do more than decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
- }
-
- spin_unlock_irq(&priv->lock);
+ /* remove all neigh connected to this mcast */
+ ipoib_del_neighs_by_gid(dev, mcast->mcmember.mgid.raw);
if (mcast->ah)
ipoib_put_ah(mcast->ah);
@@ -655,17 +640,12 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
return 0;
}
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct dst_entry *dst = skb_dst(skb);
struct ipoib_mcast *mcast;
- struct neighbour *n;
unsigned long flags;
-
- n = NULL;
- if (dst)
- n = dst_neigh_lookup_skb(dst, skb);
+ void *mgid = daddr + 4;
spin_lock_irqsave(&priv->lock, flags);
@@ -721,28 +701,29 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
out:
if (mcast && mcast->ah) {
- if (n) {
- if (!*to_ipoib_neigh(n)) {
- struct ipoib_neigh *neigh;
-
- neigh = ipoib_neigh_alloc(n, skb->dev);
- if (neigh) {
- kref_get(&mcast->ah->ref);
- neigh->ah = mcast->ah;
- list_add_tail(&neigh->list,
- &mcast->neigh_list);
- }
+ struct ipoib_neigh *neigh;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ neigh = ipoib_neigh_get(dev, daddr);
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!neigh) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ neigh = ipoib_neigh_alloc(daddr, dev);
+ spin_lock_irqsave(&priv->lock, flags);
+ if (neigh) {
+ kref_get(&mcast->ah->ref);
+ neigh->ah = mcast->ah;
+ list_add_tail(&neigh->list, &mcast->neigh_list);
}
- neigh_release(n);
}
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
+ if (neigh)
+ ipoib_neigh_put(neigh);
return;
}
unlock:
- if (n)
- neigh_release(n);
spin_unlock_irqrestore(&priv->lock, flags);
}
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c
new file mode 100644
index 000000000000..7f26e7b6c228
--- /dev/null
+++ b/drivers/input/misc/88pm80x_onkey.c
@@ -0,0 +1,168 @@
+/*
+ * Marvell 88PM80x ONKEY driver
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PM800_LONG_ONKEY_EN (1 << 0)
+#define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */
+#define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4)
+#define PM800_LONKEY_PRESS_TIME_MASK (0xF0)
+#define PM800_SW_PDOWN (1 << 5)
+
+struct pm80x_onkey_info {
+ struct input_dev *idev;
+ struct pm80x_chip *pm80x;
+ struct regmap *map;
+ int irq;
+};
+
+/* 88PM80x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm80x_onkey_handler(int irq, void *data)
+{
+ struct pm80x_onkey_info *info = data;
+ int ret = 0;
+ unsigned int val;
+
+ ret = regmap_read(info->map, PM800_STATUS_1, &val);
+ if (ret < 0) {
+ dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
+ return IRQ_NONE;
+ }
+ val &= PM800_ONKEY_STS1;
+
+ input_report_key(info->idev, KEY_POWER, val);
+ input_sync(info->idev);
+
+ return IRQ_HANDLED;
+}
+
+static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
+ pm80x_dev_resume);
+
+static int __devinit pm80x_onkey_probe(struct platform_device *pdev)
+{
+
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_onkey_info *info;
+ int err;
+
+ info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pm80x = chip;
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ info->map = info->pm80x->regmap;
+ if (!info->map) {
+ dev_err(&pdev->dev, "no regmap!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ info->idev = input_allocate_device();
+ if (!info->idev) {
+ dev_err(&pdev->dev, "Failed to allocate input dev\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ info->idev->name = "88pm80x_on";
+ info->idev->phys = "88pm80x_on/input0";
+ info->idev->id.bustype = BUS_I2C;
+ info->idev->dev.parent = &pdev->dev;
+ info->idev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, info->idev->keybit);
+
+ err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
+ IRQF_ONESHOT, "onkey", info);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, err);
+ goto out_reg;
+ }
+
+ err = input_register_device(info->idev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", err);
+ goto out_irq;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ /* Enable long onkey detection */
+ regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
+ PM800_LONG_ONKEY_EN);
+ /* Set 8-second interval */
+ regmap_update_bits(info->map, PM800_RTC_MISC3,
+ PM800_LONKEY_PRESS_TIME_MASK,
+ PM800_LONKEY_PRESS_TIME);
+
+ device_init_wakeup(&pdev->dev, 1);
+ return 0;
+
+out_irq:
+ pm80x_free_irq(info->pm80x, info->irq, info);
+out_reg:
+ input_free_device(info->idev);
+out:
+ kfree(info);
+ return err;
+}
+
+static int __devexit pm80x_onkey_remove(struct platform_device *pdev)
+{
+ struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ pm80x_free_irq(info->pm80x, info->irq, info);
+ input_unregister_device(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver pm80x_onkey_driver = {
+ .driver = {
+ .name = "88pm80x-onkey",
+ .owner = THIS_MODULE,
+ .pm = &pm80x_onkey_pm_ops,
+ },
+ .probe = pm80x_onkey_probe,
+ .remove = __devexit_p(pm80x_onkey_remove),
+};
+
+module_platform_driver(pm80x_onkey_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-onkey");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7faf4a7fcaa9..7c0f1ecfdd7a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey.
+config INPUT_88PM80X_ONKEY
+ tristate "88PM80x ONKEY support"
+ depends on MFD_88PM800
+ help
+ Support the ONKEY of Marvell 88PM80x PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 88pm80x_onkey.
+
config INPUT_AB8500_PONKEY
tristate "AB8500 Pon (PowerOn) Key"
depends on AB8500_CORE
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f55cdf4916fa..83fe6f5b77d1 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
index 84ec691c05aa..f06231b7cab1 100644
--- a/drivers/input/misc/ab8500-ponkey.c
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
ponkey->idev = input;
ponkey->ab8500 = ab8500;
- ponkey->irq_dbf = irq_dbf;
- ponkey->irq_dbr = irq_dbr;
+ ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
+ ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
input->name = "AB8500 POn(PowerOn) Key";
input->dev.parent = &pdev->dev;
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index d5b390f75c9a..14eaecea2b70 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -40,11 +40,27 @@
* Note that newer firmware allows querying device for maximum useable
* coordinates.
*/
+#define XMIN 0
+#define XMAX 6143
+#define YMIN 0
+#define YMAX 6143
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
+/* Size in bits of absolute position values reported by the hardware */
+#define ABS_POS_BITS 13
+
+/*
+ * Any position values from the hardware above the following limits are
+ * treated as "wrapped around negative" values that have been truncated to
+ * the 13-bit reporting range of the hardware. These are just reasonable
+ * guesses and can be adjusted if hardware is found that operates outside
+ * of these parameters.
+ */
+#define X_MAX_POSITIVE (((1 << ABS_POS_BITS) + XMAX) / 2)
+#define Y_MAX_POSITIVE (((1 << ABS_POS_BITS) + YMAX) / 2)
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
@@ -588,6 +604,12 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->right = (buf[0] & 0x02) ? 1 : 0;
}
+ /* Convert wrap-around values to negative */
+ if (hw->x > X_MAX_POSITIVE)
+ hw->x -= 1 << ABS_POS_BITS;
+ if (hw->y > Y_MAX_POSITIVE)
+ hw->y -= 1 << ABS_POS_BITS;
+
return 0;
}
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 09a089996ded..d7a7e54f6465 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -878,7 +878,7 @@ static int __init hp_sdc_init(void)
#endif
errstr = "IRQ not available for";
- if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM,
+ if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED,
"HP SDC", &hp_sdc))
goto err1;
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 6533f44be5bd..002041975de9 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -464,7 +464,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
t = (data[6] << 2) | ((data[7] >> 6) & 3);
if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
(features->type >= INTUOS5S && features->type <= INTUOS5L) ||
- features->type == WACOM_21UX2 || features->type == WACOM_24HD) {
+ (features->type >= WACOM_21UX2 && features->type <= WACOM_24HD)) {
t = (t << 1) | (data[1] & 1);
}
input_report_abs(input, ABS_PRESSURE, t);
@@ -614,7 +614,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_abs(input, ABS_MISC, 0);
}
} else {
- if (features->type == WACOM_21UX2) {
+ if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
input_report_key(input, BTN_0, (data[5] & 0x01));
input_report_key(input, BTN_1, (data[6] & 0x01));
input_report_key(input, BTN_2, (data[6] & 0x02));
@@ -633,6 +633,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_key(input, BTN_Z, (data[8] & 0x20));
input_report_key(input, BTN_BASE, (data[8] & 0x40));
input_report_key(input, BTN_BASE2, (data[8] & 0x80));
+
+ if (features->type == WACOM_22HD) {
+ input_report_key(input, KEY_PROG1, data[9] & 0x01);
+ input_report_key(input, KEY_PROG2, data[9] & 0x02);
+ input_report_key(input, KEY_PROG3, data[9] & 0x04);
+ }
} else {
input_report_key(input, BTN_0, (data[5] & 0x01));
input_report_key(input, BTN_1, (data[5] & 0x02));
@@ -1231,6 +1237,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case CINTIQ:
case WACOM_BEE:
case WACOM_21UX2:
+ case WACOM_22HD:
case WACOM_24HD:
sync = wacom_intuos_irq(wacom_wac);
break;
@@ -1432,6 +1439,12 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
wacom_setup_cintiq(wacom_wac);
break;
+ case WACOM_22HD:
+ __set_bit(KEY_PROG1, input_dev->keybit);
+ __set_bit(KEY_PROG2, input_dev->keybit);
+ __set_bit(KEY_PROG3, input_dev->keybit);
+ /* fall through */
+
case WACOM_21UX2:
__set_bit(BTN_A, input_dev->keybit);
__set_bit(BTN_B, input_dev->keybit);
@@ -1858,6 +1871,9 @@ static const struct wacom_features wacom_features_0xF0 =
static const struct wacom_features wacom_features_0xCC =
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xFA =
+ { "Wacom Cintiq 22HD", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047,
+ 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
static const struct wacom_features wacom_features_0x90 =
{ "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -2075,6 +2091,7 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xEF) },
{ USB_DEVICE_WACOM(0x47) },
{ USB_DEVICE_WACOM(0xF4) },
+ { USB_DEVICE_WACOM(0xFA) },
{ USB_DEVICE_LENOVO(0x6004) },
{ }
};
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index bd5d37b28714..96c185cc301e 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -73,8 +73,9 @@ enum {
INTUOS5S,
INTUOS5,
INTUOS5L,
- WACOM_24HD,
WACOM_21UX2,
+ WACOM_22HD,
+ WACOM_24HD,
CINTIQ,
WACOM_BEE,
WACOM_MO,
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 73bd2f6b82ec..1ba232cbc09d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -472,6 +472,19 @@ config TOUCHSCREEN_PENMOUNT
To compile this driver as a module, choose M here: the
module will be called penmount.
+config TOUCHSCREEN_EDT_FT5X06
+ tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
+ depends on I2C
+ help
+ Say Y here if you have an EDT "Polytouch" touchscreen based
+ on the FocalTech FT5x06 family of controllers connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called edt-ft5x06.
+
config TOUCHSCREEN_MIGOR
tristate "Renesas MIGO-R touchscreen"
depends on SH_MIGOR && I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5920c60f999d..178eb128d90f 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
+obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
new file mode 100644
index 000000000000..9afc777a40a7
--- /dev/null
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This is a driver for the EDT "Polytouch" family of touch controllers
+ * based on the FocalTech FT5x06 line of chips.
+ *
+ * Development of this driver has been sponsored by Glyn:
+ * http://www.glyn.com/Products/Displays
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/input/mt.h>
+#include <linux/input/edt-ft5x06.h>
+
+#define MAX_SUPPORT_POINTS 5
+
+#define WORK_REGISTER_THRESHOLD 0x00
+#define WORK_REGISTER_REPORT_RATE 0x08
+#define WORK_REGISTER_GAIN 0x30
+#define WORK_REGISTER_OFFSET 0x31
+#define WORK_REGISTER_NUM_X 0x33
+#define WORK_REGISTER_NUM_Y 0x34
+
+#define WORK_REGISTER_OPMODE 0x3c
+#define FACTORY_REGISTER_OPMODE 0x01
+
+#define TOUCH_EVENT_DOWN 0x00
+#define TOUCH_EVENT_UP 0x01
+#define TOUCH_EVENT_ON 0x02
+#define TOUCH_EVENT_RESERVED 0x03
+
+#define EDT_NAME_LEN 23
+#define EDT_SWITCH_MODE_RETRIES 10
+#define EDT_SWITCH_MODE_DELAY 5 /* msec */
+#define EDT_RAW_DATA_RETRIES 100
+#define EDT_RAW_DATA_DELAY 1 /* msec */
+
+struct edt_ft5x06_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ u16 num_x;
+ u16 num_y;
+
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *debug_dir;
+ u8 *raw_buffer;
+ size_t raw_bufsize;
+#endif
+
+ struct mutex mutex;
+ bool factory_mode;
+ int threshold;
+ int gain;
+ int offset;
+ int report_rate;
+
+ char name[EDT_NAME_LEN];
+};
+
+static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
+ u16 wr_len, u8 *wr_buf,
+ u16 rd_len, u8 *rd_buf)
+{
+ struct i2c_msg wrmsg[2];
+ int i = 0;
+ int ret;
+
+ if (wr_len) {
+ wrmsg[i].addr = client->addr;
+ wrmsg[i].flags = 0;
+ wrmsg[i].len = wr_len;
+ wrmsg[i].buf = wr_buf;
+ i++;
+ }
+ if (rd_len) {
+ wrmsg[i].addr = client->addr;
+ wrmsg[i].flags = I2C_M_RD;
+ wrmsg[i].len = rd_len;
+ wrmsg[i].buf = rd_buf;
+ i++;
+ }
+
+ ret = i2c_transfer(client->adapter, wrmsg, i);
+ if (ret < 0)
+ return ret;
+ if (ret != i)
+ return -EIO;
+
+ return 0;
+}
+
+static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
+ u8 *buf, int buflen)
+{
+ int i;
+ u8 crc = 0;
+
+ for (i = 0; i < buflen - 1; i++)
+ crc ^= buf[i];
+
+ if (crc != buf[buflen-1]) {
+ dev_err_ratelimited(&tsdata->client->dev,
+ "crc error: 0x%02x expected, got 0x%02x\n",
+ crc, buf[buflen-1]);
+ return false;
+ }
+
+ return true;
+}
+
+static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
+{
+ struct edt_ft5x06_ts_data *tsdata = dev_id;
+ struct device *dev = &tsdata->client->dev;
+ u8 cmd = 0xf9;
+ u8 rdbuf[26];
+ int i, type, x, y, id;
+ int error;
+
+ memset(rdbuf, 0, sizeof(rdbuf));
+
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
+ sizeof(cmd), &cmd,
+ sizeof(rdbuf), rdbuf);
+ if (error) {
+ dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
+ error);
+ goto out;
+ }
+
+ if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) {
+ dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n",
+ rdbuf[0], rdbuf[1], rdbuf[2]);
+ goto out;
+ }
+
+ if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26))
+ goto out;
+
+ for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
+ u8 *buf = &rdbuf[i * 4 + 5];
+ bool down;
+
+ type = buf[0] >> 6;
+ /* ignore Reserved events */
+ if (type == TOUCH_EVENT_RESERVED)
+ continue;
+
+ x = ((buf[0] << 8) | buf[1]) & 0x0fff;
+ y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+ id = (buf[2] >> 4) & 0x0f;
+ down = (type != TOUCH_EVENT_UP);
+
+ input_mt_slot(tsdata->input, id);
+ input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
+
+ if (!down)
+ continue;
+
+ input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
+ input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
+ }
+
+ input_mt_report_pointer_emulation(tsdata->input, true);
+ input_sync(tsdata->input);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
+ u8 addr, u8 value)
+{
+ u8 wrbuf[4];
+
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[2] = value;
+ wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+
+ return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL);
+}
+
+static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
+ u8 addr)
+{
+ u8 wrbuf[2], rdbuf[2];
+ int error;
+
+ wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+ wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+ wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+
+ error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf);
+ if (error)
+ return error;
+
+ if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+ dev_err(&tsdata->client->dev,
+ "crc error: 0x%02x expected, got 0x%02x\n",
+ wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]);
+ return -EIO;
+ }
+
+ return rdbuf[0];
+}
+
+struct edt_ft5x06_attribute {
+ struct device_attribute dattr;
+ size_t field_offset;
+ u8 limit_low;
+ u8 limit_high;
+ u8 addr;
+};
+
+#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \
+ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
+ .dattr = __ATTR(_field, _mode, \
+ edt_ft5x06_setting_show, \
+ edt_ft5x06_setting_store), \
+ .field_offset = \
+ offsetof(struct edt_ft5x06_ts_data, _field), \
+ .limit_low = _limit_low, \
+ .limit_high = _limit_high, \
+ .addr = _addr, \
+ }
+
+static ssize_t edt_ft5x06_setting_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_attribute *attr =
+ container_of(dattr, struct edt_ft5x06_attribute, dattr);
+ u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+ int val;
+ size_t count = 0;
+ int error = 0;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (tsdata->factory_mode) {
+ error = -EIO;
+ goto out;
+ }
+
+ val = edt_ft5x06_register_read(tsdata, attr->addr);
+ if (val < 0) {
+ error = val;
+ dev_err(&tsdata->client->dev,
+ "Failed to fetch attribute %s, error %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+
+ if (val != *field) {
+ dev_warn(&tsdata->client->dev,
+ "%s: read (%d) and stored value (%d) differ\n",
+ dattr->attr.name, val, *field);
+ *field = val;
+ }
+
+ count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: count;
+}
+
+static ssize_t edt_ft5x06_setting_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_attribute *attr =
+ container_of(dattr, struct edt_ft5x06_attribute, dattr);
+ u8 *field = (u8 *)((char *)tsdata + attr->field_offset);
+ unsigned int val;
+ int error;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (tsdata->factory_mode) {
+ error = -EIO;
+ goto out;
+ }
+
+ error = kstrtouint(buf, 0, &val);
+ if (error)
+ goto out;
+
+ if (val < attr->limit_low || val > attr->limit_high) {
+ error = -ERANGE;
+ goto out;
+ }
+
+ error = edt_ft5x06_register_write(tsdata, attr->addr, val);
+ if (error) {
+ dev_err(&tsdata->client->dev,
+ "Failed to update attribute %s, error: %d\n",
+ dattr->attr.name, error);
+ goto out;
+ }
+
+ *field = val;
+
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: count;
+}
+
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO,
+ WORK_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO,
+ WORK_REGISTER_REPORT_RATE, 3, 14);
+
+static struct attribute *edt_ft5x06_attrs[] = {
+ &edt_ft5x06_attr_gain.dattr.attr,
+ &edt_ft5x06_attr_offset.dattr.attr,
+ &edt_ft5x06_attr_threshold.dattr.attr,
+ &edt_ft5x06_attr_report_rate.dattr.attr,
+ NULL
+};
+
+static const struct attribute_group edt_ft5x06_attr_group = {
+ .attrs = edt_ft5x06_attrs,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_SWITCH_MODE_RETRIES;
+ int ret;
+ int error;
+
+ disable_irq(client->irq);
+
+ if (!tsdata->raw_buffer) {
+ tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
+ sizeof(u16);
+ tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
+ if (!tsdata->raw_buffer) {
+ error = -ENOMEM;
+ goto err_out;
+ }
+ }
+
+ /* mode register is 0x3c when in the work mode */
+ error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to switch to factory mode, error %d\n", error);
+ goto err_out;
+ }
+
+ tsdata->factory_mode = true;
+ do {
+ mdelay(EDT_SWITCH_MODE_DELAY);
+ /* mode register is 0x01 when in factory mode */
+ ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
+ if (ret == 0x03)
+ break;
+ } while (--retries > 0);
+
+ if (retries == 0) {
+ dev_err(&client->dev, "not in factory mode after %dms.\n",
+ EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+ error = -EIO;
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ kfree(tsdata->raw_buffer);
+ tsdata->raw_buffer = NULL;
+ tsdata->factory_mode = false;
+ enable_irq(client->irq);
+
+ return error;
+}
+
+static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_SWITCH_MODE_RETRIES;
+ int ret;
+ int error;
+
+ /* mode register is 0x01 when in the factory mode */
+ error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to switch to work mode, error: %d\n", error);
+ return error;
+ }
+
+ tsdata->factory_mode = false;
+
+ do {
+ mdelay(EDT_SWITCH_MODE_DELAY);
+ /* mode register is 0x01 when in factory mode */
+ ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
+ if (ret == 0x01)
+ break;
+ } while (--retries > 0);
+
+ if (retries == 0) {
+ dev_err(&client->dev, "not in work mode after %dms.\n",
+ EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+ tsdata->factory_mode = true;
+ return -EIO;
+ }
+
+ if (tsdata->raw_buffer)
+ kfree(tsdata->raw_buffer);
+ tsdata->raw_buffer = NULL;
+
+ /* restore parameters */
+ edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD,
+ tsdata->threshold);
+ edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN,
+ tsdata->gain);
+ edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET,
+ tsdata->offset);
+ edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE,
+ tsdata->report_rate);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
+{
+ struct edt_ft5x06_ts_data *tsdata = data;
+
+ *mode = tsdata->factory_mode;
+
+ return 0;
+};
+
+static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
+{
+ struct edt_ft5x06_ts_data *tsdata = data;
+ int retval = 0;
+
+ if (mode > 1)
+ return -ERANGE;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (mode != tsdata->factory_mode) {
+ retval = mode ? edt_ft5x06_factory_mode(tsdata) :
+ edt_ft5x06_work_mode(tsdata);
+ }
+
+ mutex_unlock(&tsdata->mutex);
+
+ return retval;
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
+ edt_ft5x06_debugfs_mode_set, "%llu\n");
+
+static int edt_ft5x06_debugfs_raw_data_open(struct inode *inode,
+ struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
+ char __user *buf, size_t count, loff_t *off)
+{
+ struct edt_ft5x06_ts_data *tsdata = file->private_data;
+ struct i2c_client *client = tsdata->client;
+ int retries = EDT_RAW_DATA_RETRIES;
+ int val, i, error;
+ size_t read = 0;
+ int colbytes;
+ char wrbuf[3];
+ u8 *rdbuf;
+
+ if (*off < 0 || *off >= tsdata->raw_bufsize)
+ return 0;
+
+ mutex_lock(&tsdata->mutex);
+
+ if (!tsdata->factory_mode || !tsdata->raw_buffer) {
+ error = -EIO;
+ goto out;
+ }
+
+ error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
+ if (error) {
+ dev_dbg(&client->dev,
+ "failed to write 0x08 register, error %d\n", error);
+ goto out;
+ }
+
+ do {
+ msleep(EDT_RAW_DATA_DELAY);
+ val = edt_ft5x06_register_read(tsdata, 0x08);
+ if (val < 1)
+ break;
+ } while (--retries > 0);
+
+ if (val < 0) {
+ error = val;
+ dev_dbg(&client->dev,
+ "failed to read 0x08 register, error %d\n", error);
+ goto out;
+ }
+
+ if (retries == 0) {
+ dev_dbg(&client->dev,
+ "timed out waiting for register to settle\n");
+ error = -ETIMEDOUT;
+ goto out;
+ }
+
+ rdbuf = tsdata->raw_buffer;
+ colbytes = tsdata->num_y * sizeof(u16);
+
+ wrbuf[0] = 0xf5;
+ wrbuf[1] = 0x0e;
+ for (i = 0; i < tsdata->num_x; i++) {
+ wrbuf[2] = i; /* column index */
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
+ sizeof(wrbuf), wrbuf,
+ colbytes, rdbuf);
+ if (error)
+ goto out;
+
+ rdbuf += colbytes;
+ }
+
+ read = min_t(size_t, count, tsdata->raw_bufsize - *off);
+ error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
+ if (!error)
+ *off += read;
+out:
+ mutex_unlock(&tsdata->mutex);
+ return error ?: read;
+};
+
+
+static const struct file_operations debugfs_raw_data_fops = {
+ .open = edt_ft5x06_debugfs_raw_data_open,
+ .read = edt_ft5x06_debugfs_raw_data_read,
+};
+
+static void __devinit
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
+{
+ tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
+ if (!tsdata->debug_dir)
+ return;
+
+ debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
+ debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
+
+ debugfs_create_file("mode", S_IRUSR | S_IWUSR,
+ tsdata->debug_dir, tsdata, &debugfs_mode_fops);
+ debugfs_create_file("raw_data", S_IRUSR,
+ tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
+}
+
+static void __devexit
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+ if (tsdata->debug_dir)
+ debugfs_remove_recursive(tsdata->debug_dir);
+}
+
+#else
+
+static inline void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+ const char *debugfs_name)
+{
+}
+
+static inline void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+}
+
+#endif /* CONFIG_DEBUGFS */
+
+
+
+static int __devinit edt_ft5x06_ts_reset(struct i2c_client *client,
+ int reset_pin)
+{
+ int error;
+
+ if (gpio_is_valid(reset_pin)) {
+ /* this pulls reset down, enabling the low active reset */
+ error = gpio_request_one(reset_pin, GPIOF_OUT_INIT_LOW,
+ "edt-ft5x06 reset");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d as reset pin, error %d\n",
+ reset_pin, error);
+ return error;
+ }
+
+ mdelay(50);
+ gpio_set_value(reset_pin, 1);
+ mdelay(100);
+ }
+
+ return 0;
+}
+
+static int __devinit edt_ft5x06_ts_identify(struct i2c_client *client,
+ char *model_name,
+ char *fw_version)
+{
+ u8 rdbuf[EDT_NAME_LEN];
+ char *p;
+ int error;
+
+ error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
+ EDT_NAME_LEN - 1, rdbuf);
+ if (error)
+ return error;
+
+ /* remove last '$' end marker */
+ rdbuf[EDT_NAME_LEN - 1] = '\0';
+ if (rdbuf[EDT_NAME_LEN - 2] == '$')
+ rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+ /* look for Model/Version separator */
+ p = strchr(rdbuf, '*');
+ if (p)
+ *p++ = '\0';
+
+ strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+ strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+
+ return 0;
+}
+
+#define EDT_ATTR_CHECKSET(name, reg) \
+ if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \
+ pdata->name <= edt_ft5x06_attr_##name.limit_high) \
+ edt_ft5x06_register_write(tsdata, reg, pdata->name)
+
+static void __devinit
+edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata,
+ const struct edt_ft5x06_platform_data *pdata)
+{
+ if (!pdata->use_parameters)
+ return;
+
+ /* pick up defaults from the platform data */
+ EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD);
+ EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN);
+ EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET);
+ EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE);
+}
+
+static void __devinit
+edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
+{
+ tsdata->threshold = edt_ft5x06_register_read(tsdata,
+ WORK_REGISTER_THRESHOLD);
+ tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN);
+ tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET);
+ tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+ WORK_REGISTER_REPORT_RATE);
+ tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X);
+ tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y);
+}
+
+static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct edt_ft5x06_platform_data *pdata =
+ client->dev.platform_data;
+ struct edt_ft5x06_ts_data *tsdata;
+ struct input_dev *input;
+ int error;
+ char fw_version[EDT_NAME_LEN];
+
+ dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ error = edt_ft5x06_ts_reset(client, pdata->reset_pin);
+ if (error)
+ return error;
+
+ if (gpio_is_valid(pdata->irq_pin)) {
+ error = gpio_request_one(pdata->irq_pin,
+ GPIOF_IN, "edt-ft5x06 irq");
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request GPIO %d, error %d\n",
+ pdata->irq_pin, error);
+ return error;
+ }
+ }
+
+ tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!tsdata || !input) {
+ dev_err(&client->dev, "failed to allocate driver data.\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ mutex_init(&tsdata->mutex);
+ tsdata->client = client;
+ tsdata->input = input;
+ tsdata->factory_mode = false;
+
+ error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version);
+ if (error) {
+ dev_err(&client->dev, "touchscreen probe failed\n");
+ goto err_free_mem;
+ }
+
+ edt_ft5x06_ts_get_defaults(tsdata, pdata);
+ edt_ft5x06_ts_get_parameters(tsdata);
+
+ dev_dbg(&client->dev,
+ "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+ tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
+
+ input->name = tsdata->name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ 0, tsdata->num_x * 64 - 1, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ 0, tsdata->num_y * 64 - 1, 0, 0);
+ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
+ if (error) {
+ dev_err(&client->dev, "Unable to init MT slots.\n");
+ goto err_free_mem;
+ }
+
+ input_set_drvdata(input, tsdata);
+ i2c_set_clientdata(client, tsdata);
+
+ error = request_threaded_irq(client->irq, NULL, edt_ft5x06_ts_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, tsdata);
+ if (error) {
+ dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ goto err_free_mem;
+ }
+
+ error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+ if (error)
+ goto err_free_irq;
+
+ error = input_register_device(input);
+ if (error)
+ goto err_remove_attrs;
+
+ edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
+ device_init_wakeup(&client->dev, 1);
+
+ dev_dbg(&client->dev,
+ "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n",
+ pdata->irq_pin, pdata->reset_pin);
+
+ return 0;
+
+err_remove_attrs:
+ sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+err_free_irq:
+ free_irq(client->irq, tsdata);
+err_free_mem:
+ input_free_device(input);
+ kfree(tsdata);
+
+ if (gpio_is_valid(pdata->irq_pin))
+ gpio_free(pdata->irq_pin);
+
+ return error;
+}
+
+static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client)
+{
+ const struct edt_ft5x06_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+
+ edt_ft5x06_ts_teardown_debugfs(tsdata);
+ sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+
+ free_irq(client->irq, tsdata);
+ input_unregister_device(tsdata->input);
+
+ if (gpio_is_valid(pdata->irq_pin))
+ gpio_free(pdata->irq_pin);
+ if (gpio_is_valid(pdata->reset_pin))
+ gpio_free(pdata->reset_pin);
+
+ kfree(tsdata->raw_buffer);
+ kfree(tsdata);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int edt_ft5x06_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int edt_ft5x06_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
+ edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+
+static const struct i2c_device_id edt_ft5x06_ts_id[] = {
+ { "edt-ft5x06", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
+
+static struct i2c_driver edt_ft5x06_ts_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "edt_ft5x06",
+ .pm = &edt_ft5x06_ts_pm_ops,
+ },
+ .id_table = edt_ft5x06_ts_id,
+ .probe = edt_ft5x06_ts_probe,
+ .remove = __devexit_p(edt_ft5x06_ts_remove),
+};
+
+module_i2c_driver(edt_ft5x06_ts_driver);
+
+MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
+MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 10f122a3a856..1eee45b69b71 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -260,15 +260,6 @@ config DM_DEBUG_BLOCK_STACK_TRACING
If unsure, say N.
-config DM_DEBUG_SPACE_MAPS
- boolean "Extra validation for thin provisioning space maps"
- depends on DM_THIN_PROVISIONING
- ---help---
- Enable this for messages that may help debug problems with the
- space maps used by thin provisioning.
-
- If unsure, say N.
-
config DM_MIRROR
tristate "Mirror target"
depends on BLK_DEV_DM
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 3f06df59fd82..664743d6a6cd 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -42,21 +42,21 @@ struct convert_context {
unsigned int offset_out;
unsigned int idx_in;
unsigned int idx_out;
- sector_t sector;
- atomic_t pending;
+ sector_t cc_sector;
+ atomic_t cc_pending;
};
/*
* per bio private data
*/
struct dm_crypt_io {
- struct dm_target *target;
+ struct crypt_config *cc;
struct bio *base_bio;
struct work_struct work;
struct convert_context ctx;
- atomic_t pending;
+ atomic_t io_pending;
int error;
sector_t sector;
struct dm_crypt_io *base_io;
@@ -109,9 +109,6 @@ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
*/
struct crypt_cpu {
struct ablkcipher_request *req;
- /* ESSIV: struct crypto_cipher *essiv_tfm */
- void *iv_private;
- struct crypto_ablkcipher *tfms[0];
};
/*
@@ -151,6 +148,10 @@ struct crypt_config {
* per_cpu_ptr() only.
*/
struct crypt_cpu __percpu *cpu;
+
+ /* ESSIV: struct crypto_cipher *essiv_tfm */
+ void *iv_private;
+ struct crypto_ablkcipher **tfms;
unsigned tfms_count;
/*
@@ -193,7 +194,7 @@ static struct crypt_cpu *this_crypt_config(struct crypt_config *cc)
*/
static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
{
- return __this_cpu_ptr(cc->cpu)->tfms[0];
+ return cc->tfms[0];
}
/*
@@ -258,7 +259,7 @@ static int crypt_iv_essiv_init(struct crypt_config *cc)
struct hash_desc desc;
struct scatterlist sg;
struct crypto_cipher *essiv_tfm;
- int err, cpu;
+ int err;
sg_init_one(&sg, cc->key, cc->key_size);
desc.tfm = essiv->hash_tfm;
@@ -268,14 +269,12 @@ static int crypt_iv_essiv_init(struct crypt_config *cc)
if (err)
return err;
- for_each_possible_cpu(cpu) {
- essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private,
+ essiv_tfm = cc->iv_private;
- err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
- crypto_hash_digestsize(essiv->hash_tfm));
- if (err)
- return err;
- }
+ err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
+ crypto_hash_digestsize(essiv->hash_tfm));
+ if (err)
+ return err;
return 0;
}
@@ -286,16 +285,14 @@ static int crypt_iv_essiv_wipe(struct crypt_config *cc)
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
unsigned salt_size = crypto_hash_digestsize(essiv->hash_tfm);
struct crypto_cipher *essiv_tfm;
- int cpu, r, err = 0;
+ int r, err = 0;
memset(essiv->salt, 0, salt_size);
- for_each_possible_cpu(cpu) {
- essiv_tfm = per_cpu_ptr(cc->cpu, cpu)->iv_private;
- r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
- if (r)
- err = r;
- }
+ essiv_tfm = cc->iv_private;
+ r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
+ if (r)
+ err = r;
return err;
}
@@ -335,8 +332,6 @@ static struct crypto_cipher *setup_essiv_cpu(struct crypt_config *cc,
static void crypt_iv_essiv_dtr(struct crypt_config *cc)
{
- int cpu;
- struct crypt_cpu *cpu_cc;
struct crypto_cipher *essiv_tfm;
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
@@ -346,15 +341,12 @@ static void crypt_iv_essiv_dtr(struct crypt_config *cc)
kzfree(essiv->salt);
essiv->salt = NULL;
- for_each_possible_cpu(cpu) {
- cpu_cc = per_cpu_ptr(cc->cpu, cpu);
- essiv_tfm = cpu_cc->iv_private;
+ essiv_tfm = cc->iv_private;
- if (essiv_tfm)
- crypto_free_cipher(essiv_tfm);
+ if (essiv_tfm)
+ crypto_free_cipher(essiv_tfm);
- cpu_cc->iv_private = NULL;
- }
+ cc->iv_private = NULL;
}
static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -363,7 +355,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
struct crypto_cipher *essiv_tfm = NULL;
struct crypto_hash *hash_tfm = NULL;
u8 *salt = NULL;
- int err, cpu;
+ int err;
if (!opts) {
ti->error = "Digest algorithm missing for ESSIV mode";
@@ -388,15 +380,13 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
cc->iv_gen_private.essiv.salt = salt;
cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
- for_each_possible_cpu(cpu) {
- essiv_tfm = setup_essiv_cpu(cc, ti, salt,
- crypto_hash_digestsize(hash_tfm));
- if (IS_ERR(essiv_tfm)) {
- crypt_iv_essiv_dtr(cc);
- return PTR_ERR(essiv_tfm);
- }
- per_cpu_ptr(cc->cpu, cpu)->iv_private = essiv_tfm;
+ essiv_tfm = setup_essiv_cpu(cc, ti, salt,
+ crypto_hash_digestsize(hash_tfm));
+ if (IS_ERR(essiv_tfm)) {
+ crypt_iv_essiv_dtr(cc);
+ return PTR_ERR(essiv_tfm);
}
+ cc->iv_private = essiv_tfm;
return 0;
@@ -410,7 +400,7 @@ bad:
static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
{
- struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
+ struct crypto_cipher *essiv_tfm = cc->iv_private;
memset(iv, 0, cc->iv_size);
*(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
@@ -664,7 +654,7 @@ static void crypt_convert_init(struct crypt_config *cc,
ctx->offset_out = 0;
ctx->idx_in = bio_in ? bio_in->bi_idx : 0;
ctx->idx_out = bio_out ? bio_out->bi_idx : 0;
- ctx->sector = sector + cc->iv_offset;
+ ctx->cc_sector = sector + cc->iv_offset;
init_completion(&ctx->restart);
}
@@ -695,12 +685,12 @@ static int crypt_convert_block(struct crypt_config *cc,
struct bio_vec *bv_out = bio_iovec_idx(ctx->bio_out, ctx->idx_out);
struct dm_crypt_request *dmreq;
u8 *iv;
- int r = 0;
+ int r;
dmreq = dmreq_of_req(cc, req);
iv = iv_of_dmreq(cc, dmreq);
- dmreq->iv_sector = ctx->sector;
+ dmreq->iv_sector = ctx->cc_sector;
dmreq->ctx = ctx;
sg_init_table(&dmreq->sg_in, 1);
sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT,
@@ -749,12 +739,12 @@ static void crypt_alloc_req(struct crypt_config *cc,
struct convert_context *ctx)
{
struct crypt_cpu *this_cc = this_crypt_config(cc);
- unsigned key_index = ctx->sector & (cc->tfms_count - 1);
+ unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1);
if (!this_cc->req)
this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
- ablkcipher_request_set_tfm(this_cc->req, this_cc->tfms[key_index]);
+ ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]);
ablkcipher_request_set_callback(this_cc->req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
@@ -769,14 +759,14 @@ static int crypt_convert(struct crypt_config *cc,
struct crypt_cpu *this_cc = this_crypt_config(cc);
int r;
- atomic_set(&ctx->pending, 1);
+ atomic_set(&ctx->cc_pending, 1);
while(ctx->idx_in < ctx->bio_in->bi_vcnt &&
ctx->idx_out < ctx->bio_out->bi_vcnt) {
crypt_alloc_req(cc, ctx);
- atomic_inc(&ctx->pending);
+ atomic_inc(&ctx->cc_pending);
r = crypt_convert_block(cc, ctx, this_cc->req);
@@ -788,19 +778,19 @@ static int crypt_convert(struct crypt_config *cc,
/* fall through*/
case -EINPROGRESS:
this_cc->req = NULL;
- ctx->sector++;
+ ctx->cc_sector++;
continue;
/* sync */
case 0:
- atomic_dec(&ctx->pending);
- ctx->sector++;
+ atomic_dec(&ctx->cc_pending);
+ ctx->cc_sector++;
cond_resched();
continue;
/* error */
default:
- atomic_dec(&ctx->pending);
+ atomic_dec(&ctx->cc_pending);
return r;
}
}
@@ -811,7 +801,7 @@ static int crypt_convert(struct crypt_config *cc,
static void dm_crypt_bio_destructor(struct bio *bio)
{
struct dm_crypt_io *io = bio->bi_private;
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
bio_free(bio, cc->bs);
}
@@ -825,7 +815,7 @@ static void dm_crypt_bio_destructor(struct bio *bio)
static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size,
unsigned *out_of_pages)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
struct bio *clone;
unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM;
@@ -884,26 +874,25 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)
}
}
-static struct dm_crypt_io *crypt_io_alloc(struct dm_target *ti,
+static struct dm_crypt_io *crypt_io_alloc(struct crypt_config *cc,
struct bio *bio, sector_t sector)
{
- struct crypt_config *cc = ti->private;
struct dm_crypt_io *io;
io = mempool_alloc(cc->io_pool, GFP_NOIO);
- io->target = ti;
+ io->cc = cc;
io->base_bio = bio;
io->sector = sector;
io->error = 0;
io->base_io = NULL;
- atomic_set(&io->pending, 0);
+ atomic_set(&io->io_pending, 0);
return io;
}
static void crypt_inc_pending(struct dm_crypt_io *io)
{
- atomic_inc(&io->pending);
+ atomic_inc(&io->io_pending);
}
/*
@@ -913,12 +902,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
*/
static void crypt_dec_pending(struct dm_crypt_io *io)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
struct bio *base_bio = io->base_bio;
struct dm_crypt_io *base_io = io->base_io;
int error = io->error;
- if (!atomic_dec_and_test(&io->pending))
+ if (!atomic_dec_and_test(&io->io_pending))
return;
mempool_free(io, cc->io_pool);
@@ -952,7 +941,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
static void crypt_endio(struct bio *clone, int error)
{
struct dm_crypt_io *io = clone->bi_private;
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
unsigned rw = bio_data_dir(clone);
if (unlikely(!bio_flagged(clone, BIO_UPTODATE) && !error))
@@ -979,7 +968,7 @@ static void crypt_endio(struct bio *clone, int error)
static void clone_init(struct dm_crypt_io *io, struct bio *clone)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
clone->bi_private = io;
clone->bi_end_io = crypt_endio;
@@ -990,7 +979,7 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone)
static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
struct bio *base_bio = io->base_bio;
struct bio *clone;
@@ -1038,7 +1027,7 @@ static void kcryptd_io(struct work_struct *work)
static void kcryptd_queue_io(struct dm_crypt_io *io)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
INIT_WORK(&io->work, kcryptd_io);
queue_work(cc->io_queue, &io->work);
@@ -1047,7 +1036,7 @@ static void kcryptd_queue_io(struct dm_crypt_io *io)
static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
{
struct bio *clone = io->ctx.bio_out;
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
if (unlikely(io->error < 0)) {
crypt_free_buffer_pages(cc, clone);
@@ -1069,7 +1058,7 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
struct bio *clone;
struct dm_crypt_io *new_io;
int crypt_finished;
@@ -1107,7 +1096,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
if (r < 0)
io->error = -EIO;
- crypt_finished = atomic_dec_and_test(&io->ctx.pending);
+ crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending);
/* Encryption was already finished, submit io now */
if (crypt_finished) {
@@ -1135,7 +1124,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
* between fragments, so switch to a new dm_crypt_io structure.
*/
if (unlikely(!crypt_finished && remaining)) {
- new_io = crypt_io_alloc(io->target, io->base_bio,
+ new_io = crypt_io_alloc(io->cc, io->base_bio,
sector);
crypt_inc_pending(new_io);
crypt_convert_init(cc, &new_io->ctx, NULL,
@@ -1169,7 +1158,7 @@ static void kcryptd_crypt_read_done(struct dm_crypt_io *io)
static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
int r = 0;
crypt_inc_pending(io);
@@ -1181,7 +1170,7 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
if (r < 0)
io->error = -EIO;
- if (atomic_dec_and_test(&io->ctx.pending))
+ if (atomic_dec_and_test(&io->ctx.cc_pending))
kcryptd_crypt_read_done(io);
crypt_dec_pending(io);
@@ -1193,7 +1182,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
struct dm_crypt_request *dmreq = async_req->data;
struct convert_context *ctx = dmreq->ctx;
struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
if (error == -EINPROGRESS) {
complete(&ctx->restart);
@@ -1208,7 +1197,7 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool);
- if (!atomic_dec_and_test(&ctx->pending))
+ if (!atomic_dec_and_test(&ctx->cc_pending))
return;
if (bio_data_dir(io->base_bio) == READ)
@@ -1229,7 +1218,7 @@ static void kcryptd_crypt(struct work_struct *work)
static void kcryptd_queue_crypt(struct dm_crypt_io *io)
{
- struct crypt_config *cc = io->target->private;
+ struct crypt_config *cc = io->cc;
INIT_WORK(&io->work, kcryptd_crypt);
queue_work(cc->crypt_queue, &io->work);
@@ -1241,7 +1230,6 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io)
static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
{
char buffer[3];
- char *endp;
unsigned int i;
buffer[2] = '\0';
@@ -1250,9 +1238,7 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
buffer[0] = *hex++;
buffer[1] = *hex++;
- key[i] = (u8)simple_strtoul(buffer, &endp, 16);
-
- if (endp != &buffer[2])
+ if (kstrtou8(buffer, 16, &key[i]))
return -EINVAL;
}
@@ -1276,29 +1262,38 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size)
}
}
-static void crypt_free_tfms(struct crypt_config *cc, int cpu)
+static void crypt_free_tfms(struct crypt_config *cc)
{
- struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
unsigned i;
+ if (!cc->tfms)
+ return;
+
for (i = 0; i < cc->tfms_count; i++)
- if (cpu_cc->tfms[i] && !IS_ERR(cpu_cc->tfms[i])) {
- crypto_free_ablkcipher(cpu_cc->tfms[i]);
- cpu_cc->tfms[i] = NULL;
+ if (cc->tfms[i] && !IS_ERR(cc->tfms[i])) {
+ crypto_free_ablkcipher(cc->tfms[i]);
+ cc->tfms[i] = NULL;
}
+
+ kfree(cc->tfms);
+ cc->tfms = NULL;
}
-static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
+static int crypt_alloc_tfms(struct crypt_config *cc, char *ciphermode)
{
- struct crypt_cpu *cpu_cc = per_cpu_ptr(cc->cpu, cpu);
unsigned i;
int err;
+ cc->tfms = kmalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *),
+ GFP_KERNEL);
+ if (!cc->tfms)
+ return -ENOMEM;
+
for (i = 0; i < cc->tfms_count; i++) {
- cpu_cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
- if (IS_ERR(cpu_cc->tfms[i])) {
- err = PTR_ERR(cpu_cc->tfms[i]);
- crypt_free_tfms(cc, cpu);
+ cc->tfms[i] = crypto_alloc_ablkcipher(ciphermode, 0, 0);
+ if (IS_ERR(cc->tfms[i])) {
+ err = PTR_ERR(cc->tfms[i]);
+ crypt_free_tfms(cc);
return err;
}
}
@@ -1309,15 +1304,14 @@ static int crypt_alloc_tfms(struct crypt_config *cc, int cpu, char *ciphermode)
static int crypt_setkey_allcpus(struct crypt_config *cc)
{
unsigned subkey_size = cc->key_size >> ilog2(cc->tfms_count);
- int cpu, err = 0, i, r;
-
- for_each_possible_cpu(cpu) {
- for (i = 0; i < cc->tfms_count; i++) {
- r = crypto_ablkcipher_setkey(per_cpu_ptr(cc->cpu, cpu)->tfms[i],
- cc->key + (i * subkey_size), subkey_size);
- if (r)
- err = r;
- }
+ int err = 0, i, r;
+
+ for (i = 0; i < cc->tfms_count; i++) {
+ r = crypto_ablkcipher_setkey(cc->tfms[i],
+ cc->key + (i * subkey_size),
+ subkey_size);
+ if (r)
+ err = r;
}
return err;
@@ -1379,9 +1373,10 @@ static void crypt_dtr(struct dm_target *ti)
cpu_cc = per_cpu_ptr(cc->cpu, cpu);
if (cpu_cc->req)
mempool_free(cpu_cc->req, cc->req_pool);
- crypt_free_tfms(cc, cpu);
}
+ crypt_free_tfms(cc);
+
if (cc->bs)
bioset_free(cc->bs);
@@ -1414,7 +1409,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
struct crypt_config *cc = ti->private;
char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
char *cipher_api = NULL;
- int cpu, ret = -EINVAL;
+ int ret = -EINVAL;
char dummy;
/* Convert to crypto api definition? */
@@ -1455,8 +1450,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
if (tmp)
DMWARN("Ignoring unexpected additional cipher options");
- cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)) +
- cc->tfms_count * sizeof(*(cc->cpu->tfms)),
+ cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)),
__alignof__(struct crypt_cpu));
if (!cc->cpu) {
ti->error = "Cannot allocate per cpu state";
@@ -1489,12 +1483,10 @@ static int crypt_ctr_cipher(struct dm_target *ti,
}
/* Allocate cipher */
- for_each_possible_cpu(cpu) {
- ret = crypt_alloc_tfms(cc, cpu, cipher_api);
- if (ret < 0) {
- ti->error = "Error allocating crypto tfm";
- goto bad;
- }
+ ret = crypt_alloc_tfms(cc, cipher_api);
+ if (ret < 0) {
+ ti->error = "Error allocating crypto tfm";
+ goto bad;
}
/* Initialize and set key */
@@ -1702,7 +1694,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ti->num_flush_requests = 1;
- ti->discard_zeroes_data_unsupported = 1;
+ ti->discard_zeroes_data_unsupported = true;
return 0;
@@ -1715,7 +1707,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
struct dm_crypt_io *io;
- struct crypt_config *cc;
+ struct crypt_config *cc = ti->private;
/*
* If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
@@ -1723,14 +1715,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
* - for REQ_DISCARD caller must use flush if IO ordering matters
*/
if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
- cc = ti->private;
bio->bi_bdev = cc->dev->bdev;
if (bio_sectors(bio))
bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
return DM_MAPIO_REMAPPED;
}
- io = crypt_io_alloc(ti, bio, dm_target_offset(ti, bio->bi_sector));
+ io = crypt_io_alloc(cc, bio, dm_target_offset(ti, bio->bi_sector));
if (bio_data_dir(io->base_bio) == READ) {
if (kcryptd_io_read(io, GFP_NOWAIT))
@@ -1742,7 +1733,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
}
static int crypt_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct crypt_config *cc = ti->private;
unsigned int sz = 0;
diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index 2dc22dddb2ae..f53846f9ab50 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -295,7 +295,7 @@ static int delay_map(struct dm_target *ti, struct bio *bio,
}
static int delay_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct delay_c *dc = ti->private;
int sz = 0;
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index aa70f7d43a1a..ebaa4f803eec 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -142,24 +142,19 @@ EXPORT_SYMBOL(dm_exception_store_type_unregister);
static int set_chunk_size(struct dm_exception_store *store,
const char *chunk_size_arg, char **error)
{
- unsigned long chunk_size_ulong;
- char *value;
+ unsigned chunk_size;
- chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10);
- if (*chunk_size_arg == '\0' || *value != '\0' ||
- chunk_size_ulong > UINT_MAX) {
+ if (kstrtouint(chunk_size_arg, 10, &chunk_size)) {
*error = "Invalid chunk size";
return -EINVAL;
}
- if (!chunk_size_ulong) {
+ if (!chunk_size) {
store->chunk_size = store->chunk_mask = store->chunk_shift = 0;
return 0;
}
- return dm_exception_store_set_chunk_size(store,
- (unsigned) chunk_size_ulong,
- error);
+ return dm_exception_store_set_chunk_size(store, chunk_size, error);
}
int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index ac49c01f1a44..cc15543a6ad7 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -333,7 +333,7 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
}
static int flakey_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
unsigned sz = 0;
struct flakey_c *fc = ti->private;
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a1a3e6df17b8..afd95986d099 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1054,6 +1054,7 @@ static void retrieve_status(struct dm_table *table,
char *outbuf, *outptr;
status_type_t type;
size_t remaining, len, used = 0;
+ unsigned status_flags = 0;
outptr = outbuf = get_result_buffer(param, param_size, &len);
@@ -1090,7 +1091,9 @@ static void retrieve_status(struct dm_table *table,
/* Get the status/table string from the target driver */
if (ti->type->status) {
- if (ti->type->status(ti, type, outptr, remaining)) {
+ if (param->flags & DM_NOFLUSH_FLAG)
+ status_flags |= DM_STATUS_NOFLUSH_FLAG;
+ if (ti->type->status(ti, type, status_flags, outptr, remaining)) {
param->flags |= DM_BUFFER_FULL_FLAG;
break;
}
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 3639eeab6042..1bf19a93eef0 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -96,7 +96,7 @@ static int linear_map(struct dm_target *ti, struct bio *bio,
}
static int linear_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct linear_c *lc = (struct linear_c *) ti->private;
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 65ebaebf502b..627d19186d5a 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -571,16 +571,6 @@ static void disk_dtr(struct dm_dirty_log *log)
destroy_log_context(lc);
}
-static int count_bits32(uint32_t *addr, unsigned size)
-{
- int count = 0, i;
-
- for (i = 0; i < size; i++) {
- count += hweight32(*(addr+i));
- }
- return count;
-}
-
static void fail_log_device(struct log_c *lc)
{
if (lc->log_dev_failed)
@@ -629,7 +619,8 @@ static int disk_resume(struct dm_dirty_log *log)
/* copy clean across to sync */
memcpy(lc->sync_bits, lc->clean_bits, size);
- lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count);
+ lc->sync_count = memweight(lc->clean_bits,
+ lc->bitset_uint32_count * sizeof(uint32_t));
lc->sync_search = 0;
/* set the correct number of regions in the header */
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 638dae048b4f..d8abb90a6c2f 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -85,6 +85,7 @@ struct multipath {
unsigned queue_io:1; /* Must we queue all I/O? */
unsigned queue_if_no_path:1; /* Queue I/O if last path fails? */
unsigned saved_queue_if_no_path:1; /* Saved state during suspension */
+ unsigned retain_attached_hw_handler:1; /* If there's already a hw_handler present, don't change it. */
unsigned pg_init_retries; /* Number of times to retry pg_init */
unsigned pg_init_count; /* Number of times pg_init called */
@@ -568,6 +569,8 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
int r;
struct pgpath *p;
struct multipath *m = ti->private;
+ struct request_queue *q = NULL;
+ const char *attached_handler_name;
/* we need at least a path arg */
if (as->argc < 1) {
@@ -586,13 +589,37 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
goto bad;
}
- if (m->hw_handler_name) {
- struct request_queue *q = bdev_get_queue(p->path.dev->bdev);
+ if (m->retain_attached_hw_handler || m->hw_handler_name)
+ q = bdev_get_queue(p->path.dev->bdev);
+
+ if (m->retain_attached_hw_handler) {
+ attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
+ if (attached_handler_name) {
+ /*
+ * Reset hw_handler_name to match the attached handler
+ * and clear any hw_handler_params associated with the
+ * ignored handler.
+ *
+ * NB. This modifies the table line to show the actual
+ * handler instead of the original table passed in.
+ */
+ kfree(m->hw_handler_name);
+ m->hw_handler_name = attached_handler_name;
+
+ kfree(m->hw_handler_params);
+ m->hw_handler_params = NULL;
+ }
+ }
+ if (m->hw_handler_name) {
+ /*
+ * Increments scsi_dh reference, even when using an
+ * already-attached handler.
+ */
r = scsi_dh_attach(q, m->hw_handler_name);
if (r == -EBUSY) {
/*
- * Already attached to different hw_handler,
+ * Already attached to different hw_handler:
* try to reattach with correct one.
*/
scsi_dh_detach(q);
@@ -760,7 +787,7 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
const char *arg_name;
static struct dm_arg _args[] = {
- {0, 5, "invalid number of feature args"},
+ {0, 6, "invalid number of feature args"},
{1, 50, "pg_init_retries must be between 1 and 50"},
{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
};
@@ -781,6 +808,11 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
continue;
}
+ if (!strcasecmp(arg_name, "retain_attached_hw_handler")) {
+ m->retain_attached_hw_handler = 1;
+ continue;
+ }
+
if (!strcasecmp(arg_name, "pg_init_retries") &&
(argc >= 1)) {
r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
@@ -1346,7 +1378,7 @@ static void multipath_resume(struct dm_target *ti)
* num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
*/
static int multipath_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
int sz = 0;
unsigned long flags;
@@ -1364,13 +1396,16 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
else {
DMEMIT("%u ", m->queue_if_no_path +
(m->pg_init_retries > 0) * 2 +
- (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2);
+ (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 +
+ m->retain_attached_hw_handler);
if (m->queue_if_no_path)
DMEMIT("queue_if_no_path ");
if (m->pg_init_retries)
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
+ if (m->retain_attached_hw_handler)
+ DMEMIT("retain_attached_hw_handler ");
}
if (!m->hw_handler_name || type == STATUSTYPE_INFO)
@@ -1656,7 +1691,7 @@ out:
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
- .version = {1, 4, 0},
+ .version = {1, 5, 0},
.module = THIS_MODULE,
.ctr = multipath_ctr,
.dtr = multipath_dtr,
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 017c34d78d61..982e3e390c45 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -11,6 +11,7 @@
#include "md.h"
#include "raid1.h"
#include "raid5.h"
+#include "raid10.h"
#include "bitmap.h"
#include <linux/device-mapper.h>
@@ -52,7 +53,10 @@ struct raid_dev {
#define DMPF_MAX_RECOVERY_RATE 0x20
#define DMPF_MAX_WRITE_BEHIND 0x40
#define DMPF_STRIPE_CACHE 0x80
-#define DMPF_REGION_SIZE 0X100
+#define DMPF_REGION_SIZE 0x100
+#define DMPF_RAID10_COPIES 0x200
+#define DMPF_RAID10_FORMAT 0x400
+
struct raid_set {
struct dm_target *ti;
@@ -76,6 +80,7 @@ static struct raid_type {
const unsigned algorithm; /* RAID algorithm. */
} raid_types[] = {
{"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},
{"raid5_la", "RAID5 (left asymmetric)", 1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
{"raid5_ra", "RAID5 (right asymmetric)", 1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -86,6 +91,17 @@ static struct raid_type {
{"raid6_nc", "RAID6 (N continue)", 2, 4, 6, ALGORITHM_ROTATING_N_CONTINUE}
};
+static unsigned raid10_md_layout_to_copies(int layout)
+{
+ return layout & 0xFF;
+}
+
+static int raid10_format_to_md_layout(char *format, unsigned copies)
+{
+ /* 1 "far" copy, and 'copies' "near" copies */
+ return (1 << 8) | (copies & 0xFF);
+}
+
static struct raid_type *get_raid_type(char *name)
{
int i;
@@ -101,20 +117,12 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
{
unsigned i;
struct raid_set *rs;
- sector_t sectors_per_dev;
if (raid_devs <= raid_type->parity_devs) {
ti->error = "Insufficient number of devices";
return ERR_PTR(-EINVAL);
}
- sectors_per_dev = ti->len;
- if ((raid_type->level > 1) &&
- sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
- ti->error = "Target length not divisible by number of data devices";
- return ERR_PTR(-EINVAL);
- }
-
rs = kzalloc(sizeof(*rs) + raid_devs * sizeof(rs->dev[0]), GFP_KERNEL);
if (!rs) {
ti->error = "Cannot allocate raid context";
@@ -128,7 +136,6 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
rs->md.raid_disks = raid_devs;
rs->md.level = raid_type->level;
rs->md.new_level = rs->md.level;
- rs->md.dev_sectors = sectors_per_dev;
rs->md.layout = raid_type->algorithm;
rs->md.new_layout = rs->md.layout;
rs->md.delta_disks = 0;
@@ -143,6 +150,7 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
* rs->md.external
* rs->md.chunk_sectors
* rs->md.new_chunk_sectors
+ * rs->md.dev_sectors
*/
return rs;
@@ -347,12 +355,20 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
* [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
* [stripe_cache <sectors>] Stripe cache size for higher RAIDs
* [region_size <sectors>] Defines granularity of bitmap
+ *
+ * RAID10-only options:
+ * [raid10_copies <# copies>] Number of copies. (Default: 2)
+ * [raid10_format <near>] Layout algorithm. (Default: near)
*/
static int parse_raid_params(struct raid_set *rs, char **argv,
unsigned num_raid_params)
{
+ char *raid10_format = "near";
+ unsigned raid10_copies = 2;
unsigned i, rebuild_cnt = 0;
unsigned long value, region_size = 0;
+ sector_t sectors_per_dev = rs->ti->len;
+ sector_t max_io_len;
char *key;
/*
@@ -422,20 +438,53 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
key = argv[i++];
+
+ /* Parameters that take a string value are checked here. */
+ if (!strcasecmp(key, "raid10_format")) {
+ if (rs->raid_type->level != 10) {
+ rs->ti->error = "'raid10_format' is an invalid parameter for this RAID type";
+ return -EINVAL;
+ }
+ if (strcmp("near", argv[i])) {
+ rs->ti->error = "Invalid 'raid10_format' value given";
+ return -EINVAL;
+ }
+ raid10_format = argv[i];
+ rs->print_flags |= DMPF_RAID10_FORMAT;
+ continue;
+ }
+
if (strict_strtoul(argv[i], 10, &value) < 0) {
rs->ti->error = "Bad numerical argument given in raid params";
return -EINVAL;
}
+ /* Parameters that take a numeric value are checked here */
if (!strcasecmp(key, "rebuild")) {
rebuild_cnt++;
- if (((rs->raid_type->level != 1) &&
- (rebuild_cnt > rs->raid_type->parity_devs)) ||
- ((rs->raid_type->level == 1) &&
- (rebuild_cnt > (rs->md.raid_disks - 1)))) {
- rs->ti->error = "Too many rebuild devices specified for given RAID type";
+
+ switch (rs->raid_type->level) {
+ case 1:
+ if (rebuild_cnt >= rs->md.raid_disks) {
+ rs->ti->error = "Too many rebuild devices specified";
+ return -EINVAL;
+ }
+ break;
+ case 4:
+ case 5:
+ case 6:
+ if (rebuild_cnt > rs->raid_type->parity_devs) {
+ rs->ti->error = "Too many rebuild devices specified for given RAID type";
+ return -EINVAL;
+ }
+ break;
+ case 10:
+ default:
+ DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name);
+ rs->ti->error = "Rebuild not supported for this RAID type";
return -EINVAL;
}
+
if (value > rs->md.raid_disks) {
rs->ti->error = "Invalid rebuild index given";
return -EINVAL;
@@ -486,7 +535,8 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
*/
value /= 2;
- if (rs->raid_type->level < 5) {
+ if ((rs->raid_type->level != 5) &&
+ (rs->raid_type->level != 6)) {
rs->ti->error = "Inappropriate argument: stripe_cache";
return -EINVAL;
}
@@ -511,6 +561,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
} else if (!strcasecmp(key, "region_size")) {
rs->print_flags |= DMPF_REGION_SIZE;
region_size = value;
+ } else if (!strcasecmp(key, "raid10_copies") &&
+ (rs->raid_type->level == 10)) {
+ if ((value < 2) || (value > 0xFF)) {
+ rs->ti->error = "Bad value for 'raid10_copies'";
+ return -EINVAL;
+ }
+ rs->print_flags |= DMPF_RAID10_COPIES;
+ raid10_copies = value;
} else {
DMERR("Unable to parse RAID parameter: %s", key);
rs->ti->error = "Unable to parse RAID parameters";
@@ -522,14 +580,33 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
if (rs->md.chunk_sectors)
- rs->ti->split_io = rs->md.chunk_sectors;
+ max_io_len = rs->md.chunk_sectors;
else
- rs->ti->split_io = region_size;
+ max_io_len = region_size;
- if (rs->md.chunk_sectors)
- rs->ti->split_io = rs->md.chunk_sectors;
- else
- rs->ti->split_io = region_size;
+ if (dm_set_target_max_io_len(rs->ti, max_io_len))
+ return -EINVAL;
+
+ if (rs->raid_type->level == 10) {
+ if (raid10_copies > rs->md.raid_disks) {
+ rs->ti->error = "Not enough devices to satisfy specification";
+ return -EINVAL;
+ }
+
+ /* (Len * #mirrors) / #devices */
+ sectors_per_dev = rs->ti->len * raid10_copies;
+ sector_div(sectors_per_dev, rs->md.raid_disks);
+
+ 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) &&
+ 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";
+ return -EINVAL;
+ }
+ rs->md.dev_sectors = sectors_per_dev;
/* Assume there are no metadata devices until the drives are parsed */
rs->md.persistent = 0;
@@ -552,6 +629,9 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
if (rs->raid_type->level == 1)
return md_raid1_congested(&rs->md, bits);
+ if (rs->raid_type->level == 10)
+ return md_raid10_congested(&rs->md, bits);
+
return md_raid5_congested(&rs->md, bits);
}
@@ -870,6 +950,9 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
case 6:
redundancy = rs->raid_type->parity_devs;
break;
+ case 10:
+ redundancy = raid10_md_layout_to_copies(mddev->layout) - 1;
+ break;
default:
ti->error = "Unknown RAID type";
return -EINVAL;
@@ -1035,12 +1118,19 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
+ if (ti->len != rs->md.array_sectors) {
+ ti->error = "Array size does not match requested target length";
+ ret = -EINVAL;
+ goto size_mismatch;
+ }
rs->callbacks.congested_fn = raid_is_congested;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
mddev_suspend(&rs->md);
return 0;
+size_mismatch:
+ md_stop(&rs->md);
bad:
context_free(rs);
@@ -1067,7 +1157,7 @@ static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_c
}
static int raid_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct raid_set *rs = ti->private;
unsigned raid_param_cnt = 1; /* at least 1 for chunksize */
@@ -1189,6 +1279,13 @@ static int raid_status(struct dm_target *ti, status_type_t type,
DMEMIT(" region_size %lu",
rs->md.bitmap_info.chunksize >> 9);
+ if (rs->print_flags & DMPF_RAID10_COPIES)
+ DMEMIT(" raid10_copies %u",
+ raid10_md_layout_to_copies(rs->md.layout));
+
+ if (rs->print_flags & DMPF_RAID10_FORMAT)
+ DMEMIT(" raid10_format near");
+
DMEMIT(" %d", rs->md.raid_disks);
for (i = 0; i < rs->md.raid_disks; i++) {
if (rs->dev[i].meta_dev)
@@ -1263,7 +1360,7 @@ static void raid_resume(struct dm_target *ti)
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
@@ -1290,6 +1387,8 @@ module_init(dm_raid_init);
module_exit(dm_raid_exit);
MODULE_DESCRIPTION(DM_NAME " raid4/5/6 target");
+MODULE_ALIAS("dm-raid1");
+MODULE_ALIAS("dm-raid10");
MODULE_ALIAS("dm-raid4");
MODULE_ALIAS("dm-raid5");
MODULE_ALIAS("dm-raid6");
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index b58b7a33914a..bc5ddba8045b 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -1081,10 +1081,14 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ti->private = ms;
- ti->split_io = dm_rh_get_region_size(ms->rh);
+
+ r = dm_set_target_max_io_len(ti, dm_rh_get_region_size(ms->rh));
+ if (r)
+ goto err_free_context;
+
ti->num_flush_requests = 1;
ti->num_discard_requests = 1;
- ti->discard_zeroes_data_unsupported = 1;
+ ti->discard_zeroes_data_unsupported = true;
ms->kmirrord_wq = alloc_workqueue("kmirrord",
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
@@ -1363,7 +1367,7 @@ static char device_status_char(struct mirror *m)
static int mirror_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
unsigned int m, sz = 0;
struct mirror_set *ms = (struct mirror_set *) ti->private;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 6f758870fc19..a143921feaf6 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -691,7 +691,7 @@ static int dm_add_exception(void *context, chunk_t old, chunk_t new)
* Return a minimum chunk size of all snapshots that have the specified origin.
* Return zero if the origin has no snapshots.
*/
-static sector_t __minimum_chunk_size(struct origin *o)
+static uint32_t __minimum_chunk_size(struct origin *o)
{
struct dm_snapshot *snap;
unsigned chunk_size = 0;
@@ -701,7 +701,7 @@ static sector_t __minimum_chunk_size(struct origin *o)
chunk_size = min_not_zero(chunk_size,
snap->store->chunk_size);
- return chunk_size;
+ return (uint32_t) chunk_size;
}
/*
@@ -1172,7 +1172,10 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->error = "Chunk size not set";
goto bad_read_metadata;
}
- ti->split_io = s->store->chunk_size;
+
+ r = dm_set_target_max_io_len(ti, s->store->chunk_size);
+ if (r)
+ goto bad_read_metadata;
return 0;
@@ -1239,7 +1242,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src,
snap_dest->store->snap = snap_dest;
snap_src->store->snap = snap_src;
- snap_dest->ti->split_io = snap_dest->store->chunk_size;
+ snap_dest->ti->max_io_len = snap_dest->store->chunk_size;
snap_dest->valid = snap_src->valid;
/*
@@ -1817,9 +1820,9 @@ static void snapshot_resume(struct dm_target *ti)
up_write(&s->lock);
}
-static sector_t get_origin_minimum_chunksize(struct block_device *bdev)
+static uint32_t get_origin_minimum_chunksize(struct block_device *bdev)
{
- sector_t min_chunksize;
+ uint32_t min_chunksize;
down_read(&_origins_lock);
min_chunksize = __minimum_chunk_size(__lookup_origin(bdev));
@@ -1838,15 +1841,15 @@ static void snapshot_merge_resume(struct dm_target *ti)
snapshot_resume(ti);
/*
- * snapshot-merge acts as an origin, so set ti->split_io
+ * snapshot-merge acts as an origin, so set ti->max_io_len
*/
- ti->split_io = get_origin_minimum_chunksize(s->origin->bdev);
+ ti->max_io_len = get_origin_minimum_chunksize(s->origin->bdev);
start_merge(s);
}
static int snapshot_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned int maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
unsigned sz = 0;
struct dm_snapshot *snap = ti->private;
@@ -2073,12 +2076,12 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
struct origin *o;
/*
- * The origin's __minimum_chunk_size() got stored in split_io
+ * The origin's __minimum_chunk_size() got stored in max_io_len
* by snapshot_merge_resume().
*/
down_read(&_origins_lock);
o = __lookup_origin(merging_snap->origin->bdev);
- for (n = 0; n < size; n += merging_snap->ti->split_io)
+ for (n = 0; n < size; n += merging_snap->ti->max_io_len)
if (__origin_write(&o->snapshots, sector + n, NULL) ==
DM_MAPIO_SUBMITTED)
must_wait = 1;
@@ -2138,18 +2141,18 @@ static int origin_map(struct dm_target *ti, struct bio *bio,
}
/*
- * Set the target "split_io" field to the minimum of all the snapshots'
+ * Set the target "max_io_len" field to the minimum of all the snapshots'
* chunk sizes.
*/
static void origin_resume(struct dm_target *ti)
{
struct dm_dev *dev = ti->private;
- ti->split_io = get_origin_minimum_chunksize(dev->bdev);
+ ti->max_io_len = get_origin_minimum_chunksize(dev->bdev);
}
-static int origin_status(struct dm_target *ti, status_type_t type, char *result,
- unsigned int maxlen)
+static int origin_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_dev *dev = ti->private;
@@ -2176,7 +2179,6 @@ static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return max_size;
bvm->bi_bdev = dev->bdev;
- bvm->bi_sector = bvm->bi_sector;
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 35c94ff24ad5..a087bf2a8d66 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -26,14 +26,12 @@ struct stripe {
struct stripe_c {
uint32_t stripes;
int stripes_shift;
- sector_t stripes_mask;
/* The size of this target / num. stripes */
sector_t stripe_width;
- /* stripe chunk size */
- uint32_t chunk_shift;
- sector_t chunk_mask;
+ uint32_t chunk_size;
+ int chunk_size_shift;
/* Needed for handling events */
struct dm_target *ti;
@@ -91,7 +89,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
/*
* Construct a striped mapping.
- * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
+ * <number of stripes> <chunk size> [<dev_path> <offset>]+
*/
static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
@@ -99,7 +97,6 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
sector_t width;
uint32_t stripes;
uint32_t chunk_size;
- char *end;
int r;
unsigned int i;
@@ -108,34 +105,23 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return -EINVAL;
}
- stripes = simple_strtoul(argv[0], &end, 10);
- if (!stripes || *end) {
+ if (kstrtouint(argv[0], 10, &stripes) || !stripes) {
ti->error = "Invalid stripe count";
return -EINVAL;
}
- chunk_size = simple_strtoul(argv[1], &end, 10);
- if (*end) {
+ if (kstrtouint(argv[1], 10, &chunk_size) || !chunk_size) {
ti->error = "Invalid chunk_size";
return -EINVAL;
}
- /*
- * chunk_size is a power of two
- */
- if (!is_power_of_2(chunk_size) ||
- (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
- ti->error = "Invalid chunk size";
- return -EINVAL;
- }
-
- if (ti->len & (chunk_size - 1)) {
+ width = ti->len;
+ if (sector_div(width, chunk_size)) {
ti->error = "Target length not divisible by "
"chunk size";
return -EINVAL;
}
- width = ti->len;
if (sector_div(width, stripes)) {
ti->error = "Target length not divisible by "
"number of stripes";
@@ -167,17 +153,21 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
if (stripes & (stripes - 1))
sc->stripes_shift = -1;
- else {
- sc->stripes_shift = ffs(stripes) - 1;
- sc->stripes_mask = ((sector_t) stripes) - 1;
- }
+ else
+ sc->stripes_shift = __ffs(stripes);
+
+ r = dm_set_target_max_io_len(ti, chunk_size);
+ if (r)
+ return r;
- ti->split_io = chunk_size;
ti->num_flush_requests = stripes;
ti->num_discard_requests = stripes;
- sc->chunk_shift = ffs(chunk_size) - 1;
- sc->chunk_mask = ((sector_t) chunk_size) - 1;
+ sc->chunk_size = chunk_size;
+ if (chunk_size & (chunk_size - 1))
+ sc->chunk_size_shift = -1;
+ else
+ sc->chunk_size_shift = __ffs(chunk_size);
/*
* Get the stripe destinations.
@@ -216,17 +206,29 @@ static void stripe_dtr(struct dm_target *ti)
static void stripe_map_sector(struct stripe_c *sc, sector_t sector,
uint32_t *stripe, sector_t *result)
{
- sector_t offset = dm_target_offset(sc->ti, sector);
- sector_t chunk = offset >> sc->chunk_shift;
+ sector_t chunk = dm_target_offset(sc->ti, sector);
+ sector_t chunk_offset;
+
+ if (sc->chunk_size_shift < 0)
+ chunk_offset = sector_div(chunk, sc->chunk_size);
+ else {
+ chunk_offset = chunk & (sc->chunk_size - 1);
+ chunk >>= sc->chunk_size_shift;
+ }
if (sc->stripes_shift < 0)
*stripe = sector_div(chunk, sc->stripes);
else {
- *stripe = chunk & sc->stripes_mask;
+ *stripe = chunk & (sc->stripes - 1);
chunk >>= sc->stripes_shift;
}
- *result = (chunk << sc->chunk_shift) | (offset & sc->chunk_mask);
+ if (sc->chunk_size_shift < 0)
+ chunk *= sc->chunk_size;
+ else
+ chunk <<= sc->chunk_size_shift;
+
+ *result = chunk + chunk_offset;
}
static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
@@ -237,9 +239,16 @@ static void stripe_map_range_sector(struct stripe_c *sc, sector_t sector,
stripe_map_sector(sc, sector, &stripe, result);
if (stripe == target_stripe)
return;
- *result &= ~sc->chunk_mask; /* round down */
+
+ /* round down */
+ sector = *result;
+ if (sc->chunk_size_shift < 0)
+ *result -= sector_div(sector, sc->chunk_size);
+ else
+ *result = sector & ~(sector_t)(sc->chunk_size - 1);
+
if (target_stripe < stripe)
- *result += sc->chunk_mask + 1; /* next chunk */
+ *result += sc->chunk_size; /* next chunk */
}
static int stripe_map_discard(struct stripe_c *sc, struct bio *bio,
@@ -302,8 +311,8 @@ static int stripe_map(struct dm_target *ti, struct bio *bio,
*
*/
-static int stripe_status(struct dm_target *ti,
- status_type_t type, char *result, unsigned int maxlen)
+static int stripe_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct stripe_c *sc = (struct stripe_c *) ti->private;
char buffer[sc->stripes + 1];
@@ -324,7 +333,7 @@ static int stripe_status(struct dm_target *ti,
case STATUSTYPE_TABLE:
DMEMIT("%d %llu", sc->stripes,
- (unsigned long long)sc->chunk_mask + 1);
+ (unsigned long long)sc->chunk_size);
for (i = 0; i < sc->stripes; i++)
DMEMIT(" %s %llu", sc->stripe[i].dev->name,
(unsigned long long)sc->stripe[i].physical_start);
@@ -391,7 +400,7 @@ static void stripe_io_hints(struct dm_target *ti,
struct queue_limits *limits)
{
struct stripe_c *sc = ti->private;
- unsigned chunk_size = (sc->chunk_mask + 1) << 9;
+ unsigned chunk_size = sc->chunk_size << SECTOR_SHIFT;
blk_limits_io_min(limits, chunk_size);
blk_limits_io_opt(limits, chunk_size * sc->stripes);
@@ -419,7 +428,7 @@ static int stripe_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
static struct target_type stripe_target = {
.name = "striped",
- .version = {1, 4, 0},
+ .version = {1, 5, 0},
.module = THIS_MODULE,
.ctr = stripe_ctr,
.dtr = stripe_dtr,
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 2e227fbf1622..f90069029aae 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1319,6 +1319,9 @@ static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
if (!ti->num_flush_requests)
continue;
+ if (ti->flush_supported)
+ return 1;
+
if (ti->type->iterate_devices &&
ti->type->iterate_devices(ti, device_flush_capable, &flush))
return 1;
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 3e2907f0bc46..693e149e9727 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011-2012 Red Hat, Inc.
*
* This file is released under the GPL.
*/
@@ -80,6 +80,12 @@
#define THIN_METADATA_CACHE_SIZE 64
#define SECTOR_TO_BLOCK_SHIFT 3
+/*
+ * 3 for btree insert +
+ * 2 for btree lookup used within space map
+ */
+#define THIN_MAX_CONCURRENT_LOCKS 5
+
/* This should be plenty */
#define SPACE_MAP_ROOT_SIZE 128
@@ -172,13 +178,20 @@ struct dm_pool_metadata {
struct rw_semaphore root_lock;
uint32_t time;
- int need_commit;
dm_block_t root;
dm_block_t details_root;
struct list_head thin_devices;
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
+ * to the previous (good) transaction failed. The only pool metadata
+ * operation possible in this state is the closing of the device.
+ */
+ bool fail_io:1;
};
struct dm_thin_device {
@@ -187,7 +200,8 @@ struct dm_thin_device {
dm_thin_id id;
int open_count;
- int changed;
+ bool changed:1;
+ bool aborted_with_changes:1;
uint64_t mapped_blocks;
uint64_t transaction_id;
uint32_t creation_time;
@@ -338,7 +352,21 @@ static int subtree_equal(void *context, void *value1_le, void *value2_le)
/*----------------------------------------------------------------*/
-static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
+static int superblock_lock_zero(struct dm_pool_metadata *pmd,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock_zero(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int superblock_lock(struct dm_pool_metadata *pmd,
+ struct dm_block **sblock)
+{
+ return dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, sblock);
+}
+
+static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result)
{
int r;
unsigned i;
@@ -365,72 +393,9 @@ static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
return dm_bm_unlock(b);
}
-static int init_pmd(struct dm_pool_metadata *pmd,
- struct dm_block_manager *bm,
- dm_block_t nr_blocks, int create)
+static void __setup_btree_details(struct dm_pool_metadata *pmd)
{
- int r;
- struct dm_space_map *sm, *data_sm;
- struct dm_transaction_manager *tm;
- struct dm_block *sblock;
-
- if (create) {
- r = dm_tm_create_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &tm, &sm, &sblock);
- if (r < 0) {
- DMERR("tm_create_with_sm failed");
- return r;
- }
-
- data_sm = dm_sm_disk_create(tm, nr_blocks);
- if (IS_ERR(data_sm)) {
- DMERR("sm_disk_create failed");
- dm_tm_unlock(tm, sblock);
- r = PTR_ERR(data_sm);
- goto bad;
- }
- } else {
- struct thin_disk_superblock *disk_super = NULL;
- size_t space_map_root_offset =
- offsetof(struct thin_disk_superblock, metadata_space_map_root);
-
- r = dm_tm_open_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, space_map_root_offset,
- SPACE_MAP_ROOT_SIZE, &tm, &sm, &sblock);
- if (r < 0) {
- DMERR("tm_open_with_sm failed");
- return r;
- }
-
- disk_super = dm_block_data(sblock);
- data_sm = dm_sm_disk_open(tm, disk_super->data_space_map_root,
- sizeof(disk_super->data_space_map_root));
- if (IS_ERR(data_sm)) {
- DMERR("sm_disk_open failed");
- r = PTR_ERR(data_sm);
- goto bad;
- }
- }
-
-
- r = dm_tm_unlock(tm, sblock);
- if (r < 0) {
- DMERR("couldn't unlock superblock");
- goto bad_data_sm;
- }
-
- pmd->bm = bm;
- pmd->metadata_sm = sm;
- pmd->data_sm = data_sm;
- pmd->tm = tm;
- pmd->nb_tm = dm_tm_create_non_blocking_clone(tm);
- if (!pmd->nb_tm) {
- DMERR("could not create clone tm");
- r = -ENOMEM;
- goto bad_data_sm;
- }
-
- pmd->info.tm = tm;
+ pmd->info.tm = pmd->tm;
pmd->info.levels = 2;
pmd->info.value_type.context = pmd->data_sm;
pmd->info.value_type.size = sizeof(__le64);
@@ -441,7 +406,7 @@ static int init_pmd(struct dm_pool_metadata *pmd,
memcpy(&pmd->nb_info, &pmd->info, sizeof(pmd->nb_info));
pmd->nb_info.tm = pmd->nb_tm;
- pmd->tl_info.tm = tm;
+ pmd->tl_info.tm = pmd->tm;
pmd->tl_info.levels = 1;
pmd->tl_info.value_type.context = &pmd->info;
pmd->tl_info.value_type.size = sizeof(__le64);
@@ -449,7 +414,7 @@ static int init_pmd(struct dm_pool_metadata *pmd,
pmd->tl_info.value_type.dec = subtree_dec;
pmd->tl_info.value_type.equal = subtree_equal;
- pmd->bl_info.tm = tm;
+ pmd->bl_info.tm = pmd->tm;
pmd->bl_info.levels = 1;
pmd->bl_info.value_type.context = pmd->data_sm;
pmd->bl_info.value_type.size = sizeof(__le64);
@@ -457,48 +422,266 @@ static int init_pmd(struct dm_pool_metadata *pmd,
pmd->bl_info.value_type.dec = data_block_dec;
pmd->bl_info.value_type.equal = data_block_equal;
- pmd->details_info.tm = tm;
+ pmd->details_info.tm = pmd->tm;
pmd->details_info.levels = 1;
pmd->details_info.value_type.context = NULL;
pmd->details_info.value_type.size = sizeof(struct disk_device_details);
pmd->details_info.value_type.inc = NULL;
pmd->details_info.value_type.dec = NULL;
pmd->details_info.value_type.equal = NULL;
+}
- pmd->root = 0;
+static int __write_initial_superblock(struct dm_pool_metadata *pmd)
+{
+ int r;
+ struct dm_block *sblock;
+ size_t metadata_len, data_len;
+ struct thin_disk_superblock *disk_super;
+ sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
- init_rwsem(&pmd->root_lock);
- pmd->time = 0;
- pmd->need_commit = 0;
- pmd->details_root = 0;
- pmd->trans_id = 0;
- pmd->flags = 0;
- INIT_LIST_HEAD(&pmd->thin_devices);
+ if (bdev_size > THIN_METADATA_MAX_SECTORS)
+ bdev_size = THIN_METADATA_MAX_SECTORS;
+
+ r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_root_size(pmd->data_sm, &data_len);
+ if (r < 0)
+ return r;
+
+ r = dm_sm_commit(pmd->data_sm);
+ if (r < 0)
+ return r;
+
+ r = dm_tm_pre_commit(pmd->tm);
+ if (r < 0)
+ return r;
+
+ r = superblock_lock_zero(pmd, &sblock);
+ if (r)
+ return r;
+
+ disk_super = dm_block_data(sblock);
+ disk_super->flags = 0;
+ memset(disk_super->uuid, 0, sizeof(disk_super->uuid));
+ disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
+ disk_super->version = cpu_to_le32(THIN_VERSION);
+ disk_super->time = 0;
+ disk_super->trans_id = 0;
+ disk_super->held_root = 0;
+
+ r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
+ metadata_len);
+ if (r < 0)
+ goto bad_locked;
+
+ r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
+ data_len);
+ if (r < 0)
+ goto bad_locked;
+
+ disk_super->data_mapping_root = cpu_to_le64(pmd->root);
+ disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+ disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+ disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
+ disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
+
+ return dm_tm_commit(pmd->tm, sblock);
+
+bad_locked:
+ dm_bm_unlock(sblock);
+ return r;
+}
+
+static int __format_metadata(struct dm_pool_metadata *pmd)
+{
+ int r;
+
+ r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &pmd->tm, &pmd->metadata_sm);
+ if (r < 0) {
+ DMERR("tm_create_with_sm failed");
+ return r;
+ }
+
+ pmd->data_sm = dm_sm_disk_create(pmd->tm, 0);
+ if (IS_ERR(pmd->data_sm)) {
+ DMERR("sm_disk_create failed");
+ r = PTR_ERR(pmd->data_sm);
+ goto bad_cleanup_tm;
+ }
+
+ pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
+ if (!pmd->nb_tm) {
+ DMERR("could not create non-blocking clone tm");
+ r = -ENOMEM;
+ goto bad_cleanup_data_sm;
+ }
+
+ __setup_btree_details(pmd);
+
+ r = dm_btree_empty(&pmd->info, &pmd->root);
+ if (r < 0)
+ goto bad_cleanup_nb_tm;
+
+ r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
+ if (r < 0) {
+ DMERR("couldn't create devices root");
+ goto bad_cleanup_nb_tm;
+ }
+
+ r = __write_initial_superblock(pmd);
+ if (r)
+ goto bad_cleanup_nb_tm;
return 0;
-bad_data_sm:
- dm_sm_destroy(data_sm);
-bad:
- dm_tm_destroy(tm);
- dm_sm_destroy(sm);
+bad_cleanup_nb_tm:
+ dm_tm_destroy(pmd->nb_tm);
+bad_cleanup_data_sm:
+ dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+ dm_tm_destroy(pmd->tm);
+ dm_sm_destroy(pmd->metadata_sm);
+
+ return r;
+}
+
+static int __check_incompat_features(struct thin_disk_superblock *disk_super,
+ struct dm_pool_metadata *pmd)
+{
+ uint32_t features;
+
+ features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
+ if (features) {
+ DMERR("could not access metadata due to unsupported optional features (%lx).",
+ (unsigned long)features);
+ return -EINVAL;
+ }
+
+ /*
+ * Check for read-only metadata to skip the following RDWR checks.
+ */
+ if (get_disk_ro(pmd->bdev->bd_disk))
+ return 0;
+
+ features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
+ if (features) {
+ DMERR("could not access metadata RDWR due to unsupported optional features (%lx).",
+ (unsigned long)features);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __open_metadata(struct dm_pool_metadata *pmd)
+{
+ int r;
+ struct dm_block *sblock;
+ struct thin_disk_superblock *disk_super;
+
+ r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ &sb_validator, &sblock);
+ if (r < 0) {
+ DMERR("couldn't read superblock");
+ return r;
+ }
+
+ disk_super = dm_block_data(sblock);
+
+ r = __check_incompat_features(disk_super, pmd);
+ if (r < 0)
+ goto bad_unlock_sblock;
+
+ r = dm_tm_open_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+ disk_super->metadata_space_map_root,
+ sizeof(disk_super->metadata_space_map_root),
+ &pmd->tm, &pmd->metadata_sm);
+ if (r < 0) {
+ DMERR("tm_open_with_sm failed");
+ goto bad_unlock_sblock;
+ }
+
+ pmd->data_sm = dm_sm_disk_open(pmd->tm, disk_super->data_space_map_root,
+ sizeof(disk_super->data_space_map_root));
+ if (IS_ERR(pmd->data_sm)) {
+ DMERR("sm_disk_open failed");
+ r = PTR_ERR(pmd->data_sm);
+ goto bad_cleanup_tm;
+ }
+
+ pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
+ if (!pmd->nb_tm) {
+ DMERR("could not create non-blocking clone tm");
+ r = -ENOMEM;
+ goto bad_cleanup_data_sm;
+ }
+
+ __setup_btree_details(pmd);
+ return dm_bm_unlock(sblock);
+
+bad_cleanup_data_sm:
+ dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+ dm_tm_destroy(pmd->tm);
+ dm_sm_destroy(pmd->metadata_sm);
+bad_unlock_sblock:
+ dm_bm_unlock(sblock);
+
+ return r;
+}
+
+static int __open_or_format_metadata(struct dm_pool_metadata *pmd, bool format_device)
+{
+ int r, unformatted;
+
+ r = __superblock_all_zeroes(pmd->bm, &unformatted);
+ if (r)
+ return r;
+
+ if (unformatted)
+ return format_device ? __format_metadata(pmd) : -EPERM;
+
+ return __open_metadata(pmd);
+}
+
+static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool format_device)
+{
+ int r;
+
+ pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE,
+ THIN_METADATA_CACHE_SIZE,
+ THIN_MAX_CONCURRENT_LOCKS);
+ if (IS_ERR(pmd->bm)) {
+ DMERR("could not create block manager");
+ return PTR_ERR(pmd->bm);
+ }
+
+ r = __open_or_format_metadata(pmd, format_device);
+ if (r)
+ dm_block_manager_destroy(pmd->bm);
return r;
}
+static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd)
+{
+ dm_sm_destroy(pmd->data_sm);
+ dm_sm_destroy(pmd->metadata_sm);
+ dm_tm_destroy(pmd->nb_tm);
+ dm_tm_destroy(pmd->tm);
+ dm_block_manager_destroy(pmd->bm);
+}
+
static int __begin_transaction(struct dm_pool_metadata *pmd)
{
int r;
- u32 features;
struct thin_disk_superblock *disk_super;
struct dm_block *sblock;
/*
- * __maybe_commit_transaction() resets these
- */
- WARN_ON(pmd->need_commit);
-
- /*
* We re-read the superblock every time. Shouldn't need to do this
* really.
*/
@@ -515,32 +698,8 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
pmd->flags = le32_to_cpu(disk_super->flags);
pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
- features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
- if (features) {
- DMERR("could not access metadata due to "
- "unsupported optional features (%lx).",
- (unsigned long)features);
- r = -EINVAL;
- goto out;
- }
-
- /*
- * Check for read-only metadata to skip the following RDWR checks.
- */
- if (get_disk_ro(pmd->bdev->bd_disk))
- goto out;
-
- features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
- if (features) {
- DMERR("could not access metadata RDWR due to "
- "unsupported optional features (%lx).",
- (unsigned long)features);
- r = -EINVAL;
- }
-
-out:
dm_bm_unlock(sblock);
- return r;
+ return 0;
}
static int __write_changed_details(struct dm_pool_metadata *pmd)
@@ -573,8 +732,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd)
list_del(&td->list);
kfree(td);
}
-
- pmd->need_commit = 1;
}
return 0;
@@ -582,9 +739,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd)
static int __commit_transaction(struct dm_pool_metadata *pmd)
{
- /*
- * FIXME: Associated pool should be made read-only on failure.
- */
int r;
size_t metadata_len, data_len;
struct thin_disk_superblock *disk_super;
@@ -597,31 +751,27 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
r = __write_changed_details(pmd);
if (r < 0)
- goto out;
-
- if (!pmd->need_commit)
- goto out;
+ return r;
r = dm_sm_commit(pmd->data_sm);
if (r < 0)
- goto out;
+ return r;
r = dm_tm_pre_commit(pmd->tm);
if (r < 0)
- goto out;
+ return r;
r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
if (r < 0)
- goto out;
+ return r;
r = dm_sm_root_size(pmd->data_sm, &data_len);
if (r < 0)
- goto out;
+ return r;
- r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &sblock);
+ r = superblock_lock(pmd, &sblock);
if (r)
- goto out;
+ return r;
disk_super = dm_block_data(sblock);
disk_super->time = cpu_to_le32(pmd->time);
@@ -640,12 +790,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
if (r < 0)
goto out_locked;
- r = dm_tm_commit(pmd->tm, sblock);
- if (!r)
- pmd->need_commit = 0;
-
-out:
- return r;
+ return dm_tm_commit(pmd->tm, sblock);
out_locked:
dm_bm_unlock(sblock);
@@ -653,15 +798,11 @@ out_locked:
}
struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
- sector_t data_block_size)
+ sector_t data_block_size,
+ bool format_device)
{
int r;
- struct thin_disk_superblock *disk_super;
struct dm_pool_metadata *pmd;
- sector_t bdev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
- struct dm_block_manager *bm;
- int create;
- struct dm_block *sblock;
pmd = kmalloc(sizeof(*pmd), GFP_KERNEL);
if (!pmd) {
@@ -669,90 +810,28 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
return ERR_PTR(-ENOMEM);
}
- /*
- * Max hex locks:
- * 3 for btree insert +
- * 2 for btree lookup used within space map
- */
- bm = dm_block_manager_create(bdev, THIN_METADATA_BLOCK_SIZE,
- THIN_METADATA_CACHE_SIZE, 5);
- if (!bm) {
- DMERR("could not create block manager");
- kfree(pmd);
- return ERR_PTR(-ENOMEM);
- }
-
- r = superblock_all_zeroes(bm, &create);
- if (r) {
- dm_block_manager_destroy(bm);
- kfree(pmd);
- return ERR_PTR(r);
- }
-
+ 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;
- r = init_pmd(pmd, bm, 0, create);
+ r = __create_persistent_data_objects(pmd, format_device);
if (r) {
- dm_block_manager_destroy(bm);
kfree(pmd);
return ERR_PTR(r);
}
- pmd->bdev = bdev;
-
- if (!create) {
- r = __begin_transaction(pmd);
- if (r < 0)
- goto bad;
- return pmd;
- }
-
- /*
- * Create.
- */
- r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &sblock);
- if (r)
- goto bad;
-
- if (bdev_size > THIN_METADATA_MAX_SECTORS)
- bdev_size = THIN_METADATA_MAX_SECTORS;
-
- disk_super = dm_block_data(sblock);
- disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
- disk_super->version = cpu_to_le32(THIN_VERSION);
- disk_super->time = 0;
- disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
- disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
- disk_super->data_block_size = cpu_to_le32(data_block_size);
-
- r = dm_bm_unlock(sblock);
- if (r < 0)
- goto bad;
-
- r = dm_btree_empty(&pmd->info, &pmd->root);
- if (r < 0)
- goto bad;
-
- r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
- if (r < 0) {
- DMERR("couldn't create devices root");
- goto bad;
- }
- pmd->flags = 0;
- pmd->need_commit = 1;
- r = dm_pool_commit_metadata(pmd);
+ r = __begin_transaction(pmd);
if (r < 0) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
- goto bad;
+ if (dm_pool_metadata_close(pmd) < 0)
+ DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+ return ERR_PTR(r);
}
return pmd;
-
-bad:
- if (dm_pool_metadata_close(pmd) < 0)
- DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
- return ERR_PTR(r);
}
int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
@@ -778,18 +857,17 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
return -EBUSY;
}
- r = __commit_transaction(pmd);
- if (r < 0)
- DMWARN("%s: __commit_transaction() failed, error = %d",
- __func__, r);
+ if (!pmd->read_only && !pmd->fail_io) {
+ r = __commit_transaction(pmd);
+ if (r < 0)
+ DMWARN("%s: __commit_transaction() failed, error = %d",
+ __func__, r);
+ }
- dm_tm_destroy(pmd->tm);
- dm_tm_destroy(pmd->nb_tm);
- dm_block_manager_destroy(pmd->bm);
- dm_sm_destroy(pmd->metadata_sm);
- dm_sm_destroy(pmd->data_sm);
- kfree(pmd);
+ if (!pmd->fail_io)
+ __destroy_persistent_data_objects(pmd);
+ kfree(pmd);
return 0;
}
@@ -850,6 +928,7 @@ static int __open_device(struct dm_pool_metadata *pmd,
(*td)->id = dev;
(*td)->open_count = 1;
(*td)->changed = changed;
+ (*td)->aborted_with_changes = false;
(*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks);
(*td)->transaction_id = le64_to_cpu(details_le.transaction_id);
(*td)->creation_time = le32_to_cpu(details_le.creation_time);
@@ -911,10 +990,11 @@ static int __create_thin(struct dm_pool_metadata *pmd,
int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __create_thin(pmd, dev);
+ if (!pmd->fail_io)
+ r = __create_thin(pmd, dev);
up_write(&pmd->root_lock);
return r;
@@ -1001,10 +1081,11 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd,
dm_thin_id dev,
dm_thin_id origin)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __create_snap(pmd, dev, origin);
+ if (!pmd->fail_io)
+ r = __create_snap(pmd, dev, origin);
up_write(&pmd->root_lock);
return r;
@@ -1037,18 +1118,17 @@ static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
if (r)
return r;
- pmd->need_commit = 1;
-
return 0;
}
int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
dm_thin_id dev)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __delete_device(pmd, dev);
+ if (!pmd->fail_io)
+ r = __delete_device(pmd, dev);
up_write(&pmd->root_lock);
return r;
@@ -1058,28 +1138,40 @@ int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
uint64_t current_id,
uint64_t new_id)
{
+ int r = -EINVAL;
+
down_write(&pmd->root_lock);
+
+ if (pmd->fail_io)
+ goto out;
+
if (pmd->trans_id != current_id) {
- up_write(&pmd->root_lock);
DMERR("mismatched transaction id");
- return -EINVAL;
+ goto out;
}
pmd->trans_id = new_id;
- pmd->need_commit = 1;
+ r = 0;
+
+out:
up_write(&pmd->root_lock);
- return 0;
+ return r;
}
int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
uint64_t *result)
{
+ int r = -EINVAL;
+
down_read(&pmd->root_lock);
- *result = pmd->trans_id;
+ if (!pmd->fail_io) {
+ *result = pmd->trans_id;
+ r = 0;
+ }
up_read(&pmd->root_lock);
- return 0;
+ return r;
}
static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
@@ -1108,8 +1200,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
dm_tm_dec(pmd->tm, held_root);
dm_tm_unlock(pmd->tm, copy);
- pmd->need_commit = 1;
-
return -EBUSY;
}
@@ -1131,29 +1221,25 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
/*
* Write the held root into the superblock.
*/
- r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &sblock);
+ r = superblock_lock(pmd, &sblock);
if (r) {
dm_tm_dec(pmd->tm, held_root);
- pmd->need_commit = 1;
return r;
}
disk_super = dm_block_data(sblock);
disk_super->held_root = cpu_to_le64(held_root);
dm_bm_unlock(sblock);
-
- pmd->need_commit = 1;
-
return 0;
}
int dm_pool_reserve_metadata_snap(struct dm_pool_metadata *pmd)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __reserve_metadata_snap(pmd);
+ if (!pmd->fail_io)
+ r = __reserve_metadata_snap(pmd);
up_write(&pmd->root_lock);
return r;
@@ -1166,15 +1252,13 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
struct dm_block *sblock, *copy;
dm_block_t held_root;
- r = dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &sblock);
+ r = superblock_lock(pmd, &sblock);
if (r)
return r;
disk_super = dm_block_data(sblock);
held_root = le64_to_cpu(disk_super->held_root);
disk_super->held_root = cpu_to_le64(0);
- pmd->need_commit = 1;
dm_bm_unlock(sblock);
@@ -1197,10 +1281,11 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __release_metadata_snap(pmd);
+ if (!pmd->fail_io)
+ r = __release_metadata_snap(pmd);
up_write(&pmd->root_lock);
return r;
@@ -1227,10 +1312,11 @@ static int __get_metadata_snap(struct dm_pool_metadata *pmd,
int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
down_read(&pmd->root_lock);
- r = __get_metadata_snap(pmd, result);
+ if (!pmd->fail_io)
+ r = __get_metadata_snap(pmd, result);
up_read(&pmd->root_lock);
return r;
@@ -1239,10 +1325,11 @@ int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
struct dm_thin_device **td)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __open_device(pmd, dev, 0, td);
+ if (!pmd->fail_io)
+ r = __open_device(pmd, dev, 0, td);
up_write(&pmd->root_lock);
return r;
@@ -1262,7 +1349,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td)
return td->id;
}
-static int __snapshotted_since(struct dm_thin_device *td, uint32_t time)
+static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time)
{
return td->snapshotted_time > time;
}
@@ -1270,28 +1357,31 @@ static int __snapshotted_since(struct dm_thin_device *td, uint32_t time)
int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
int can_block, struct dm_thin_lookup_result *result)
{
- int r;
+ int r = -EINVAL;
uint64_t block_time = 0;
__le64 value;
struct dm_pool_metadata *pmd = td->pmd;
dm_block_t keys[2] = { td->id, block };
+ struct dm_btree_info *info;
if (can_block) {
down_read(&pmd->root_lock);
- r = dm_btree_lookup(&pmd->info, pmd->root, keys, &value);
- if (!r)
- block_time = le64_to_cpu(value);
- up_read(&pmd->root_lock);
-
- } else if (down_read_trylock(&pmd->root_lock)) {
- r = dm_btree_lookup(&pmd->nb_info, pmd->root, keys, &value);
- if (!r)
- block_time = le64_to_cpu(value);
- up_read(&pmd->root_lock);
-
- } else
+ info = &pmd->info;
+ } else if (down_read_trylock(&pmd->root_lock))
+ info = &pmd->nb_info;
+ else
return -EWOULDBLOCK;
+ if (pmd->fail_io)
+ goto out;
+
+ r = dm_btree_lookup(info, pmd->root, keys, &value);
+ if (!r)
+ block_time = le64_to_cpu(value);
+
+out:
+ up_read(&pmd->root_lock);
+
if (!r) {
dm_block_t exception_block;
uint32_t exception_time;
@@ -1312,7 +1402,6 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
struct dm_pool_metadata *pmd = td->pmd;
dm_block_t keys[2] = { td->id, block };
- pmd->need_commit = 1;
value = cpu_to_le64(pack_block_time(data_block, pmd->time));
__dm_bless_for_disk(&value);
@@ -1321,10 +1410,9 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
if (r)
return r;
- if (inserted) {
+ td->changed = 1;
+ if (inserted)
td->mapped_blocks++;
- td->changed = 1;
- }
return 0;
}
@@ -1332,10 +1420,11 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
dm_block_t data_block)
{
- int r;
+ int r = -EINVAL;
down_write(&td->pmd->root_lock);
- r = __insert(td, block, data_block);
+ if (!td->pmd->fail_io)
+ r = __insert(td, block, data_block);
up_write(&td->pmd->root_lock);
return r;
@@ -1353,31 +1442,51 @@ static int __remove(struct dm_thin_device *td, dm_block_t block)
td->mapped_blocks--;
td->changed = 1;
- pmd->need_commit = 1;
return 0;
}
int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
{
- int r;
+ int r = -EINVAL;
down_write(&td->pmd->root_lock);
- r = __remove(td, block);
+ if (!td->pmd->fail_io)
+ r = __remove(td, block);
up_write(&td->pmd->root_lock);
return r;
}
-int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+bool dm_thin_changed_this_transaction(struct dm_thin_device *td)
{
int r;
- down_write(&pmd->root_lock);
+ down_read(&td->pmd->root_lock);
+ r = td->changed;
+ up_read(&td->pmd->root_lock);
- r = dm_sm_new_block(pmd->data_sm, result);
- pmd->need_commit = 1;
+ return r;
+}
+
+bool dm_thin_aborted_changes(struct dm_thin_device *td)
+{
+ bool r;
+ down_read(&td->pmd->root_lock);
+ r = td->aborted_with_changes;
+ up_read(&td->pmd->root_lock);
+
+ return r;
+}
+
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+ int r = -EINVAL;
+
+ down_write(&pmd->root_lock);
+ if (!pmd->fail_io)
+ r = dm_sm_new_block(pmd->data_sm, result);
up_write(&pmd->root_lock);
return r;
@@ -1385,9 +1494,11 @@ int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
+ if (pmd->fail_io)
+ goto out;
r = __commit_transaction(pmd);
if (r <= 0)
@@ -1402,12 +1513,41 @@ out:
return r;
}
+static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd)
+{
+ struct dm_thin_device *td;
+
+ list_for_each_entry(td, &pmd->thin_devices, list)
+ td->aborted_with_changes = td->changed;
+}
+
+int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
+{
+ int r = -EINVAL;
+
+ down_write(&pmd->root_lock);
+ if (pmd->fail_io)
+ goto out;
+
+ __set_abort_with_changes_flags(pmd);
+ __destroy_persistent_data_objects(pmd);
+ r = __create_persistent_data_objects(pmd, false);
+ if (r)
+ pmd->fail_io = true;
+
+out:
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
down_read(&pmd->root_lock);
- r = dm_sm_get_nr_free(pmd->data_sm, result);
+ if (!pmd->fail_io)
+ r = dm_sm_get_nr_free(pmd->data_sm, result);
up_read(&pmd->root_lock);
return r;
@@ -1416,10 +1556,11 @@ int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *resul
int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
down_read(&pmd->root_lock);
- r = dm_sm_get_nr_free(pmd->metadata_sm, result);
+ if (!pmd->fail_io)
+ r = dm_sm_get_nr_free(pmd->metadata_sm, result);
up_read(&pmd->root_lock);
return r;
@@ -1428,10 +1569,11 @@ int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
down_read(&pmd->root_lock);
- r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
+ if (!pmd->fail_io)
+ r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
up_read(&pmd->root_lock);
return r;
@@ -1448,10 +1590,11 @@ int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result)
int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
down_read(&pmd->root_lock);
- r = dm_sm_get_nr_blocks(pmd->data_sm, result);
+ if (!pmd->fail_io)
+ r = dm_sm_get_nr_blocks(pmd->data_sm, result);
up_read(&pmd->root_lock);
return r;
@@ -1459,13 +1602,17 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
{
+ int r = -EINVAL;
struct dm_pool_metadata *pmd = td->pmd;
down_read(&pmd->root_lock);
- *result = td->mapped_blocks;
+ if (!pmd->fail_io) {
+ *result = td->mapped_blocks;
+ r = 0;
+ }
up_read(&pmd->root_lock);
- return 0;
+ return r;
}
static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
@@ -1487,11 +1634,12 @@ static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
dm_block_t *result)
{
- int r;
+ int r = -EINVAL;
struct dm_pool_metadata *pmd = td->pmd;
down_read(&pmd->root_lock);
- r = __highest_block(td, result);
+ if (!pmd->fail_io)
+ r = __highest_block(td, result);
up_read(&pmd->root_lock);
return r;
@@ -1514,20 +1662,25 @@ static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
return -EINVAL;
}
- r = dm_sm_extend(pmd->data_sm, new_count - old_count);
- if (!r)
- pmd->need_commit = 1;
-
- return r;
+ return dm_sm_extend(pmd->data_sm, new_count - old_count);
}
int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
{
- int r;
+ int r = -EINVAL;
down_write(&pmd->root_lock);
- r = __resize_data_dev(pmd, new_count);
+ if (!pmd->fail_io)
+ r = __resize_data_dev(pmd, new_count);
up_write(&pmd->root_lock);
return r;
}
+
+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);
+}
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index b88918ccdaf6..0cecc3702885 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -38,7 +38,8 @@ typedef uint64_t dm_thin_id;
* Reopens or creates a new, empty metadata volume.
*/
struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
- sector_t data_block_size);
+ sector_t data_block_size,
+ bool format_device);
int dm_pool_metadata_close(struct dm_pool_metadata *pmd);
@@ -79,6 +80,16 @@ int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
int dm_pool_commit_metadata(struct dm_pool_metadata *pmd);
/*
+ * Discards all uncommitted changes. Rereads the superblock, rolling back
+ * to the last good transaction. Thin devices remain open.
+ * dm_thin_aborted_changes() tells you if they had uncommitted changes.
+ *
+ * If this call fails it's only useful to call dm_pool_metadata_close().
+ * All other methods will fail with -EINVAL.
+ */
+int dm_pool_abort_metadata(struct dm_pool_metadata *pmd);
+
+/*
* Set/get userspace transaction id.
*/
int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
@@ -119,7 +130,7 @@ dm_thin_id dm_thin_dev_id(struct dm_thin_device *td);
struct dm_thin_lookup_result {
dm_block_t block;
- int shared;
+ unsigned shared:1;
};
/*
@@ -147,6 +158,10 @@ int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block);
/*
* Queries.
*/
+bool dm_thin_changed_this_transaction(struct dm_thin_device *td);
+
+bool dm_thin_aborted_changes(struct dm_thin_device *td);
+
int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
dm_block_t *highest_mapped);
@@ -171,6 +186,12 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result);
*/
int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
+/*
+ * Flicks the underlying block manager into read only mode, so you know
+ * that nothing is changing.
+ */
+void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd);
+
/*----------------------------------------------------------------*/
#endif
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 68694da0d21d..af1fc3b2c2ad 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -1,10 +1,11 @@
/*
- * Copyright (C) 2011 Red Hat UK.
+ * Copyright (C) 2011-2012 Red Hat UK.
*
* This file is released under the GPL.
*/
#include "dm-thin-metadata.h"
+#include "dm.h"
#include <linux/device-mapper.h>
#include <linux/dm-io.h>
@@ -19,7 +20,7 @@
/*
* Tunable constants
*/
-#define ENDIO_HOOK_POOL_SIZE 10240
+#define ENDIO_HOOK_POOL_SIZE 1024
#define DEFERRED_SET_SIZE 64
#define MAPPING_POOL_SIZE 1024
#define PRISON_CELLS 1024
@@ -496,12 +497,27 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
*/
struct dm_thin_new_mapping;
+/*
+ * The pool runs in 3 modes. Ordered in degraded order for comparisons.
+ */
+enum pool_mode {
+ PM_WRITE, /* metadata may be changed */
+ PM_READ_ONLY, /* metadata may not be changed */
+ PM_FAIL, /* all I/O fails */
+};
+
struct pool_features {
+ enum pool_mode mode;
+
unsigned zero_new_blocks:1;
unsigned discard_enabled:1;
unsigned discard_passdown:1;
};
+struct thin_c;
+typedef void (*process_bio_fn)(struct thin_c *tc, struct bio *bio);
+typedef void (*process_mapping_fn)(struct dm_thin_new_mapping *m);
+
struct pool {
struct list_head list;
struct dm_target *ti; /* Only set if a pool target is bound */
@@ -510,10 +526,9 @@ struct pool {
struct block_device *md_dev;
struct dm_pool_metadata *pmd;
- uint32_t sectors_per_block;
- unsigned block_shift;
- dm_block_t offset_mask;
dm_block_t low_water_blocks;
+ uint32_t sectors_per_block;
+ int sectors_per_block_shift;
struct pool_features pf;
unsigned low_water_triggered:1; /* A dm event has been sent */
@@ -526,8 +541,8 @@ struct pool {
struct work_struct worker;
struct delayed_work waker;
- unsigned ref_count;
unsigned long last_commit_jiffies;
+ unsigned ref_count;
spinlock_t lock;
struct bio_list deferred_bios;
@@ -543,8 +558,17 @@ struct pool {
struct dm_thin_new_mapping *next_mapping;
mempool_t *mapping_pool;
mempool_t *endio_hook_pool;
+
+ process_bio_fn process_bio;
+ process_bio_fn process_discard;
+
+ process_mapping_fn process_prepared_mapping;
+ process_mapping_fn process_prepared_discard;
};
+static enum pool_mode get_pool_mode(struct pool *pool);
+static void set_pool_mode(struct pool *pool, enum pool_mode mode);
+
/*
* Target context for a pool.
*/
@@ -679,16 +703,28 @@ static void requeue_io(struct thin_c *tc)
static dm_block_t get_bio_block(struct thin_c *tc, struct bio *bio)
{
- return bio->bi_sector >> tc->pool->block_shift;
+ sector_t block_nr = bio->bi_sector;
+
+ if (tc->pool->sectors_per_block_shift < 0)
+ (void) sector_div(block_nr, tc->pool->sectors_per_block);
+ else
+ block_nr >>= tc->pool->sectors_per_block_shift;
+
+ return block_nr;
}
static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
{
struct pool *pool = tc->pool;
+ sector_t bi_sector = bio->bi_sector;
bio->bi_bdev = tc->pool_dev->bdev;
- bio->bi_sector = (block << pool->block_shift) +
- (bio->bi_sector & pool->offset_mask);
+ if (tc->pool->sectors_per_block_shift < 0)
+ bio->bi_sector = (block * pool->sectors_per_block) +
+ sector_div(bi_sector, pool->sectors_per_block);
+ else
+ bio->bi_sector = (block << pool->sectors_per_block_shift) |
+ (bi_sector & (pool->sectors_per_block - 1));
}
static void remap_to_origin(struct thin_c *tc, struct bio *bio)
@@ -696,21 +732,39 @@ static void remap_to_origin(struct thin_c *tc, struct bio *bio)
bio->bi_bdev = tc->origin_dev->bdev;
}
+static int bio_triggers_commit(struct thin_c *tc, struct bio *bio)
+{
+ return (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) &&
+ dm_thin_changed_this_transaction(tc->td);
+}
+
static void issue(struct thin_c *tc, struct bio *bio)
{
struct pool *pool = tc->pool;
unsigned long flags;
+ if (!bio_triggers_commit(tc, bio)) {
+ generic_make_request(bio);
+ return;
+ }
+
/*
- * Batch together any FUA/FLUSH bios we find and then issue
- * a single commit for them in process_deferred_bios().
+ * Complete bio with an error if earlier I/O caused changes to
+ * the metadata that can't be committed e.g, due to I/O errors
+ * on the metadata device.
*/
- if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
- spin_lock_irqsave(&pool->lock, flags);
- bio_list_add(&pool->deferred_flush_bios, bio);
- spin_unlock_irqrestore(&pool->lock, flags);
- } else
- generic_make_request(bio);
+ if (dm_thin_aborted_changes(tc->td)) {
+ bio_io_error(bio);
+ return;
+ }
+
+ /*
+ * Batch together any bios that trigger commits and then issue a
+ * single commit for them in process_deferred_bios().
+ */
+ spin_lock_irqsave(&pool->lock, flags);
+ bio_list_add(&pool->deferred_flush_bios, bio);
+ spin_unlock_irqrestore(&pool->lock, flags);
}
static void remap_to_origin_and_issue(struct thin_c *tc, struct bio *bio)
@@ -847,6 +901,14 @@ static void cell_defer_except(struct thin_c *tc, struct dm_bio_prison_cell *cell
wake_worker(pool);
}
+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;
+ cell_error(m->cell);
+ list_del(&m->list);
+ mempool_free(m, m->tc->pool->mapping_pool);
+}
static void process_prepared_mapping(struct dm_thin_new_mapping *m)
{
struct thin_c *tc = m->tc;
@@ -859,7 +921,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
if (m->err) {
cell_error(m->cell);
- return;
+ goto out;
}
/*
@@ -871,7 +933,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
if (r) {
DMERR("dm_thin_insert_block() failed");
cell_error(m->cell);
- return;
+ goto out;
}
/*
@@ -886,22 +948,25 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
} else
cell_defer(tc, m->cell, m->data_block);
+out:
list_del(&m->list);
mempool_free(m, tc->pool->mapping_pool);
}
-static void process_prepared_discard(struct dm_thin_new_mapping *m)
+static void process_prepared_discard_fail(struct dm_thin_new_mapping *m)
{
- int r;
struct thin_c *tc = m->tc;
- r = dm_thin_remove_block(tc->td, m->virt_block);
- if (r)
- DMERR("dm_thin_remove_block() failed");
+ bio_io_error(m->bio);
+ cell_defer_except(tc, m->cell);
+ cell_defer_except(tc, m->cell2);
+ mempool_free(m, tc->pool->mapping_pool);
+}
+
+static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
+{
+ struct thin_c *tc = m->tc;
- /*
- * Pass the discard down to the underlying device?
- */
if (m->pass_discard)
remap_and_issue(tc, m->bio, m->data_block);
else
@@ -912,8 +977,20 @@ static void process_prepared_discard(struct dm_thin_new_mapping *m)
mempool_free(m, tc->pool->mapping_pool);
}
+static void process_prepared_discard(struct dm_thin_new_mapping *m)
+{
+ int r;
+ struct thin_c *tc = m->tc;
+
+ r = dm_thin_remove_block(tc->td, m->virt_block);
+ if (r)
+ DMERR("dm_thin_remove_block() failed");
+
+ process_prepared_discard_passdown(m);
+}
+
static void process_prepared(struct pool *pool, struct list_head *head,
- void (*fn)(struct dm_thin_new_mapping *))
+ process_mapping_fn *fn)
{
unsigned long flags;
struct list_head maps;
@@ -925,7 +1002,7 @@ static void process_prepared(struct pool *pool, struct list_head *head,
spin_unlock_irqrestore(&pool->lock, flags);
list_for_each_entry_safe(m, tmp, &maps, list)
- fn(m);
+ (*fn)(m);
}
/*
@@ -933,9 +1010,7 @@ static void process_prepared(struct pool *pool, struct list_head *head,
*/
static int io_overlaps_block(struct pool *pool, struct bio *bio)
{
- return !(bio->bi_sector & pool->offset_mask) &&
- (bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT));
-
+ return bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT);
}
static int io_overwrites_block(struct pool *pool, struct bio *bio)
@@ -1093,6 +1168,35 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
}
}
+static int commit(struct pool *pool)
+{
+ int r;
+
+ r = dm_pool_commit_metadata(pool->pmd);
+ if (r)
+ DMERR("commit failed, error = %d", r);
+
+ return r;
+}
+
+/*
+ * A non-zero return indicates read_only or fail_io mode.
+ * Many callers don't care about the return value.
+ */
+static int commit_or_fallback(struct pool *pool)
+{
+ int r;
+
+ if (get_pool_mode(pool) != PM_WRITE)
+ return -EINVAL;
+
+ r = commit(pool);
+ if (r)
+ set_pool_mode(pool, PM_READ_ONLY);
+
+ return r;
+}
+
static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
{
int r;
@@ -1121,12 +1225,7 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
* Try to commit to see if that will free up some
* more space.
*/
- r = dm_pool_commit_metadata(pool->pmd);
- if (r) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
- return r;
- }
+ (void) commit_or_fallback(pool);
r = dm_pool_get_free_block_count(pool->pmd, &free_blocks);
if (r)
@@ -1218,7 +1317,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
*/
m = get_next_mapping(pool);
m->tc = tc;
- m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown;
+ m->pass_discard = (!lookup_result.shared) && pool->pf.discard_passdown;
m->virt_block = block;
m->data_block = lookup_result.block;
m->cell = cell;
@@ -1234,15 +1333,10 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
}
} else {
/*
- * This path is hit if people are ignoring
- * limits->discard_granularity. It ignores any
- * part of the discard that is in a subsequent
- * block.
+ * The DM core makes sure that the discard doesn't span
+ * a block boundary. So we submit the discard of a
+ * partial block appropriately.
*/
- sector_t offset = bio->bi_sector - (block << pool->block_shift);
- unsigned remaining = (pool->sectors_per_block - offset) << 9;
- bio->bi_size = min(bio->bi_size, remaining);
-
cell_release_singleton(cell, bio);
cell_release_singleton(cell2, bio);
if ((!lookup_result.shared) && pool->pf.discard_passdown)
@@ -1310,7 +1404,7 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio,
if (bio_detain(pool->prison, &key, bio, &cell))
return;
- if (bio_data_dir(bio) == WRITE)
+ if (bio_data_dir(bio) == WRITE && bio->bi_size)
break_sharing(tc, bio, block, &key, lookup_result, cell);
else {
struct dm_thin_endio_hook *h = dm_get_mapinfo(bio)->ptr;
@@ -1362,6 +1456,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block
default:
DMERR("%s: alloc_data_block() failed, error = %d", __func__, r);
+ set_pool_mode(tc->pool, PM_READ_ONLY);
cell_error(cell);
break;
}
@@ -1419,6 +1514,49 @@ static void process_bio(struct thin_c *tc, struct bio *bio)
}
}
+static void process_bio_read_only(struct thin_c *tc, struct bio *bio)
+{
+ int r;
+ int rw = bio_data_dir(bio);
+ dm_block_t block = get_bio_block(tc, bio);
+ struct dm_thin_lookup_result lookup_result;
+
+ r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
+ switch (r) {
+ case 0:
+ if (lookup_result.shared && (rw == WRITE) && bio->bi_size)
+ bio_io_error(bio);
+ else
+ remap_and_issue(tc, bio, lookup_result.block);
+ break;
+
+ case -ENODATA:
+ if (rw != READ) {
+ bio_io_error(bio);
+ break;
+ }
+
+ if (tc->origin_dev) {
+ remap_to_origin_and_issue(tc, bio);
+ break;
+ }
+
+ zero_fill_bio(bio);
+ bio_endio(bio, 0);
+ break;
+
+ default:
+ DMERR("dm_thin_find_block() failed, error = %d", r);
+ bio_io_error(bio);
+ break;
+ }
+}
+
+static void process_bio_fail(struct thin_c *tc, struct bio *bio)
+{
+ bio_io_error(bio);
+}
+
static int need_commit_due_to_time(struct pool *pool)
{
return jiffies < pool->last_commit_jiffies ||
@@ -1430,7 +1568,6 @@ static void process_deferred_bios(struct pool *pool)
unsigned long flags;
struct bio *bio;
struct bio_list bios;
- int r;
bio_list_init(&bios);
@@ -1457,9 +1594,9 @@ static void process_deferred_bios(struct pool *pool)
}
if (bio->bi_rw & REQ_DISCARD)
- process_discard(tc, bio);
+ pool->process_discard(tc, bio);
else
- process_bio(tc, bio);
+ pool->process_bio(tc, bio);
}
/*
@@ -1475,10 +1612,7 @@ static void process_deferred_bios(struct pool *pool)
if (bio_list_empty(&bios) && !need_commit_due_to_time(pool))
return;
- r = dm_pool_commit_metadata(pool->pmd);
- if (r) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
+ if (commit_or_fallback(pool)) {
while ((bio = bio_list_pop(&bios)))
bio_io_error(bio);
return;
@@ -1493,8 +1627,8 @@ static void do_worker(struct work_struct *ws)
{
struct pool *pool = container_of(ws, struct pool, worker);
- process_prepared(pool, &pool->prepared_mappings, process_prepared_mapping);
- process_prepared(pool, &pool->prepared_discards, process_prepared_discard);
+ process_prepared(pool, &pool->prepared_mappings, &pool->process_prepared_mapping);
+ process_prepared(pool, &pool->prepared_discards, &pool->process_prepared_discard);
process_deferred_bios(pool);
}
@@ -1511,6 +1645,52 @@ static void do_waker(struct work_struct *ws)
/*----------------------------------------------------------------*/
+static enum pool_mode get_pool_mode(struct pool *pool)
+{
+ return pool->pf.mode;
+}
+
+static void set_pool_mode(struct pool *pool, enum pool_mode mode)
+{
+ int r;
+
+ pool->pf.mode = mode;
+
+ switch (mode) {
+ case PM_FAIL:
+ DMERR("switching pool to failure mode");
+ pool->process_bio = process_bio_fail;
+ pool->process_discard = process_bio_fail;
+ pool->process_prepared_mapping = process_prepared_mapping_fail;
+ pool->process_prepared_discard = process_prepared_discard_fail;
+ break;
+
+ case PM_READ_ONLY:
+ DMERR("switching pool to read-only mode");
+ r = dm_pool_abort_metadata(pool->pmd);
+ if (r) {
+ DMERR("aborting transaction failed");
+ set_pool_mode(pool, PM_FAIL);
+ } else {
+ dm_pool_metadata_read_only(pool->pmd);
+ pool->process_bio = process_bio_read_only;
+ pool->process_discard = process_discard;
+ pool->process_prepared_mapping = process_prepared_mapping_fail;
+ pool->process_prepared_discard = process_prepared_discard_passdown;
+ }
+ break;
+
+ case PM_WRITE:
+ pool->process_bio = process_bio;
+ pool->process_discard = process_discard;
+ pool->process_prepared_mapping = process_prepared_mapping;
+ pool->process_prepared_discard = process_prepared_discard;
+ break;
+ }
+}
+
+/*----------------------------------------------------------------*/
+
/*
* Mapping functions.
*/
@@ -1556,6 +1736,12 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio,
struct dm_thin_lookup_result result;
map_context->ptr = thin_hook_bio(tc, bio);
+
+ if (get_pool_mode(tc->pool) == PM_FAIL) {
+ bio_io_error(bio);
+ return DM_MAPIO_SUBMITTED;
+ }
+
if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) {
thin_defer_bio(tc, bio);
return DM_MAPIO_SUBMITTED;
@@ -1592,14 +1778,35 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio,
break;
case -ENODATA:
+ if (get_pool_mode(tc->pool) == PM_READ_ONLY) {
+ /*
+ * This block isn't provisioned, and we have no way
+ * of doing so. Just error it.
+ */
+ bio_io_error(bio);
+ r = DM_MAPIO_SUBMITTED;
+ break;
+ }
+ /* fall through */
+
+ case -EWOULDBLOCK:
/*
* In future, the failed dm_thin_find_block above could
* provide the hint to load the metadata into cache.
*/
- case -EWOULDBLOCK:
thin_defer_bio(tc, bio);
r = DM_MAPIO_SUBMITTED;
break;
+
+ default:
+ /*
+ * Must always call bio_io_error on failure.
+ * dm_thin_find_block can fail with -EINVAL if the
+ * pool is switched to fail-io mode.
+ */
+ bio_io_error(bio);
+ r = DM_MAPIO_SUBMITTED;
+ break;
}
return r;
@@ -1636,15 +1843,26 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
{
struct pool_c *pt = ti->private;
+ /*
+ * We want to make sure that degraded pools are never upgraded.
+ */
+ enum pool_mode old_mode = pool->pf.mode;
+ enum pool_mode new_mode = pt->pf.mode;
+
+ if (old_mode > new_mode)
+ new_mode = old_mode;
+
pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
pool->pf = pt->pf;
+ set_pool_mode(pool, new_mode);
/*
* If discard_passdown was enabled verify that the data device
* supports discards. Disable discard_passdown if not; otherwise
* -EOPNOTSUPP will be returned.
*/
+ /* FIXME: pull this out into a sep fn. */
if (pt->pf.discard_passdown) {
struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
if (!q || !blk_queue_discard(q)) {
@@ -1670,6 +1888,7 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
/* Initialize pool features. */
static void pool_features_init(struct pool_features *pf)
{
+ pf->mode = PM_WRITE;
pf->zero_new_blocks = 1;
pf->discard_enabled = 1;
pf->discard_passdown = 1;
@@ -1700,14 +1919,16 @@ static struct kmem_cache *_endio_hook_cache;
static struct pool *pool_create(struct mapped_device *pool_md,
struct block_device *metadata_dev,
- unsigned long block_size, char **error)
+ unsigned long block_size,
+ int read_only, char **error)
{
int r;
void *err_p;
struct pool *pool;
struct dm_pool_metadata *pmd;
+ bool format_device = read_only ? false : true;
- pmd = dm_pool_metadata_open(metadata_dev, block_size);
+ pmd = dm_pool_metadata_open(metadata_dev, block_size, format_device);
if (IS_ERR(pmd)) {
*error = "Error creating metadata object";
return (struct pool *)pmd;
@@ -1722,8 +1943,10 @@ static struct pool *pool_create(struct mapped_device *pool_md,
pool->pmd = pmd;
pool->sectors_per_block = block_size;
- pool->block_shift = ffs(block_size) - 1;
- pool->offset_mask = block_size - 1;
+ if (block_size & (block_size - 1))
+ pool->sectors_per_block_shift = -1;
+ else
+ pool->sectors_per_block_shift = __ffs(block_size);
pool->low_water_blocks = 0;
pool_features_init(&pool->pf);
pool->prison = prison_create(PRISON_CELLS);
@@ -1822,25 +2045,29 @@ static void __pool_dec(struct pool *pool)
static struct pool *__pool_find(struct mapped_device *pool_md,
struct block_device *metadata_dev,
- unsigned long block_size, char **error,
- int *created)
+ unsigned long block_size, int read_only,
+ char **error, int *created)
{
struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
if (pool) {
- if (pool->pool_md != pool_md)
+ if (pool->pool_md != pool_md) {
+ *error = "metadata device already in use by a pool";
return ERR_PTR(-EBUSY);
+ }
__pool_inc(pool);
} else {
pool = __pool_table_lookup(pool_md);
if (pool) {
- if (pool->md_dev != metadata_dev)
+ if (pool->md_dev != metadata_dev) {
+ *error = "different pool cannot replace a pool";
return ERR_PTR(-EINVAL);
+ }
__pool_inc(pool);
} else {
- pool = pool_create(pool_md, metadata_dev, block_size, error);
+ pool = pool_create(pool_md, metadata_dev, block_size, read_only, error);
*created = 1;
}
}
@@ -1891,19 +2118,23 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
arg_name = dm_shift_arg(as);
argc--;
- if (!strcasecmp(arg_name, "skip_block_zeroing")) {
+ if (!strcasecmp(arg_name, "skip_block_zeroing"))
pf->zero_new_blocks = 0;
- continue;
- } else if (!strcasecmp(arg_name, "ignore_discard")) {
+
+ else if (!strcasecmp(arg_name, "ignore_discard"))
pf->discard_enabled = 0;
- continue;
- } else if (!strcasecmp(arg_name, "no_discard_passdown")) {
+
+ else if (!strcasecmp(arg_name, "no_discard_passdown"))
pf->discard_passdown = 0;
- continue;
- }
- ti->error = "Unrecognised pool feature requested";
- r = -EINVAL;
+ else if (!strcasecmp(arg_name, "read_only"))
+ pf->mode = PM_READ_ONLY;
+
+ else {
+ ti->error = "Unrecognised pool feature requested";
+ r = -EINVAL;
+ break;
+ }
}
return r;
@@ -1967,7 +2198,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
if (kstrtoul(argv[2], 10, &block_size) || !block_size ||
block_size < DATA_DEV_BLOCK_SIZE_MIN_SECTORS ||
block_size > DATA_DEV_BLOCK_SIZE_MAX_SECTORS ||
- !is_power_of_2(block_size)) {
+ block_size & (DATA_DEV_BLOCK_SIZE_MIN_SECTORS - 1)) {
ti->error = "Invalid block size";
r = -EINVAL;
goto out;
@@ -1996,7 +2227,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
- block_size, &ti->error, &pool_created);
+ block_size, pf.mode == PM_READ_ONLY, &ti->error, &pool_created);
if (IS_ERR(pool)) {
r = PTR_ERR(pool);
goto out_free_pt;
@@ -2014,6 +2245,15 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto out_flags_changed;
}
+ /*
+ * The block layer requires discard_granularity to be a power of 2.
+ */
+ if (pf.discard_enabled && !is_power_of_2(block_size)) {
+ ti->error = "Discard support must be disabled when the block size is not a power of 2";
+ r = -EINVAL;
+ goto out_flags_changed;
+ }
+
pt->pool = pool;
pt->ti = ti;
pt->metadata_dev = metadata_dev;
@@ -2033,7 +2273,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
* stacking of discard limits (this keeps the pool and
* thin devices' discard limits consistent).
*/
- ti->discards_supported = 1;
+ ti->discards_supported = true;
}
ti->private = pt;
@@ -2093,7 +2333,8 @@ static int pool_preresume(struct dm_target *ti)
int r;
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
- dm_block_t data_size, sb_data_size;
+ sector_t data_size = ti->len;
+ dm_block_t sb_data_size;
/*
* Take control of the pool object.
@@ -2102,7 +2343,8 @@ static int pool_preresume(struct dm_target *ti)
if (r)
return r;
- data_size = ti->len >> pool->block_shift;
+ (void) sector_div(data_size, pool->sectors_per_block);
+
r = dm_pool_get_data_dev_size(pool->pmd, &sb_data_size);
if (r) {
DMERR("failed to retrieve data device size");
@@ -2111,22 +2353,19 @@ static int pool_preresume(struct dm_target *ti)
if (data_size < sb_data_size) {
DMERR("pool target too small, is %llu blocks (expected %llu)",
- data_size, sb_data_size);
+ (unsigned long long)data_size, sb_data_size);
return -EINVAL;
} else if (data_size > sb_data_size) {
r = dm_pool_resize_data_dev(pool->pmd, data_size);
if (r) {
DMERR("failed to resize data device");
+ /* FIXME Stricter than necessary: Rollback transaction instead here */
+ set_pool_mode(pool, PM_READ_ONLY);
return r;
}
- r = dm_pool_commit_metadata(pool->pmd);
- if (r) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
- return r;
- }
+ (void) commit_or_fallback(pool);
}
return 0;
@@ -2149,19 +2388,12 @@ static void pool_resume(struct dm_target *ti)
static void pool_postsuspend(struct dm_target *ti)
{
- int r;
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
cancel_delayed_work(&pool->waker);
flush_workqueue(pool->wq);
-
- r = dm_pool_commit_metadata(pool->pmd);
- if (r < 0) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
- /* FIXME: invalidate device? error the next FUA or FLUSH bio ?*/
- }
+ (void) commit_or_fallback(pool);
}
static int check_arg_count(unsigned argc, unsigned args_required)
@@ -2295,12 +2527,7 @@ static int process_reserve_metadata_snap_mesg(unsigned argc, char **argv, struct
if (r)
return r;
- r = dm_pool_commit_metadata(pool->pmd);
- if (r) {
- DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
- __func__, r);
- return r;
- }
+ (void) commit_or_fallback(pool);
r = dm_pool_reserve_metadata_snap(pool->pmd);
if (r)
@@ -2361,25 +2588,41 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
else
DMWARN("Unrecognised thin pool target message received: %s", argv[0]);
- if (!r) {
- r = dm_pool_commit_metadata(pool->pmd);
- if (r)
- DMERR("%s message: dm_pool_commit_metadata() failed, error = %d",
- argv[0], r);
- }
+ if (!r)
+ (void) commit_or_fallback(pool);
return r;
}
+static void emit_flags(struct pool_features *pf, char *result,
+ unsigned sz, unsigned maxlen)
+{
+ unsigned count = !pf->zero_new_blocks + !pf->discard_enabled +
+ !pf->discard_passdown + (pf->mode == PM_READ_ONLY);
+ DMEMIT("%u ", count);
+
+ if (!pf->zero_new_blocks)
+ DMEMIT("skip_block_zeroing ");
+
+ if (!pf->discard_enabled)
+ DMEMIT("ignore_discard ");
+
+ if (!pf->discard_passdown)
+ DMEMIT("no_discard_passdown ");
+
+ if (pf->mode == PM_READ_ONLY)
+ DMEMIT("read_only ");
+}
+
/*
* Status line is:
* <transaction id> <used metadata sectors>/<total metadata sectors>
* <used data sectors>/<total data sectors> <held metadata root>
*/
static int pool_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
- int r, count;
+ int r;
unsigned sz = 0;
uint64_t transaction_id;
dm_block_t nr_free_blocks_data;
@@ -2394,6 +2637,15 @@ static int pool_status(struct dm_target *ti, status_type_t type,
switch (type) {
case STATUSTYPE_INFO:
+ if (get_pool_mode(pool) == PM_FAIL) {
+ DMEMIT("Fail");
+ break;
+ }
+
+ /* Commit to ensure statistics aren't out-of-date */
+ if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti))
+ (void) commit_or_fallback(pool);
+
r = dm_pool_get_metadata_transaction_id(pool->pmd,
&transaction_id);
if (r)
@@ -2429,9 +2681,19 @@ static int pool_status(struct dm_target *ti, status_type_t type,
(unsigned long long)nr_blocks_data);
if (held_root)
- DMEMIT("%llu", held_root);
+ DMEMIT("%llu ", held_root);
+ else
+ DMEMIT("- ");
+
+ if (pool->pf.mode == PM_READ_ONLY)
+ DMEMIT("ro ");
+ else
+ DMEMIT("rw ");
+
+ if (pool->pf.discard_enabled && pool->pf.discard_passdown)
+ DMEMIT("discard_passdown");
else
- DMEMIT("-");
+ DMEMIT("no_discard_passdown");
break;
@@ -2441,20 +2703,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);
-
- count = !pool->pf.zero_new_blocks + !pool->pf.discard_enabled +
- !pt->pf.discard_passdown;
- DMEMIT("%u ", count);
-
- if (!pool->pf.zero_new_blocks)
- DMEMIT("skip_block_zeroing ");
-
- if (!pool->pf.discard_enabled)
- DMEMIT("ignore_discard ");
-
- if (!pt->pf.discard_passdown)
- DMEMIT("no_discard_passdown ");
-
+ emit_flags(&pt->pf, result, sz, maxlen);
break;
}
@@ -2492,7 +2741,8 @@ static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
/*
* This is just a hint, and not enforced. We have to cope with
- * bios that overlap 2 blocks.
+ * bios that cover a block partially. A discard that spans a block
+ * boundary is not sent to this target.
*/
limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
limits->discard_zeroes_data = pool->pf.zero_new_blocks;
@@ -2513,7 +2763,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -2618,20 +2868,31 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
__pool_inc(tc->pool);
+ if (get_pool_mode(tc->pool) == PM_FAIL) {
+ ti->error = "Couldn't open thin device, Pool is in fail mode";
+ goto bad_thin_open;
+ }
+
r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td);
if (r) {
ti->error = "Couldn't open thin internal device";
goto bad_thin_open;
}
- ti->split_io = tc->pool->sectors_per_block;
+ r = dm_set_target_max_io_len(ti, tc->pool->sectors_per_block);
+ if (r)
+ goto bad_thin_open;
+
ti->num_flush_requests = 1;
+ ti->flush_supported = true;
/* In case the pool supports discards, pass them on. */
if (tc->pool->pf.discard_enabled) {
- ti->discards_supported = 1;
+ ti->discards_supported = true;
ti->num_discard_requests = 1;
- ti->discard_zeroes_data_unsupported = 1;
+ ti->discard_zeroes_data_unsupported = true;
+ /* Discard requests must be split on a block boundary */
+ ti->split_discard_requests = true;
}
dm_put(pool_md);
@@ -2712,7 +2973,7 @@ static void thin_postsuspend(struct dm_target *ti)
* <nr mapped sectors> <highest mapped sector>
*/
static int thin_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
int r;
ssize_t sz = 0;
@@ -2720,6 +2981,11 @@ static int thin_status(struct dm_target *ti, status_type_t type,
char buf[BDEVNAME_SIZE];
struct thin_c *tc = ti->private;
+ if (get_pool_mode(tc->pool) == PM_FAIL) {
+ DMEMIT("Fail");
+ return 0;
+ }
+
if (!tc->td)
DMEMIT("-");
else {
@@ -2757,19 +3023,21 @@ static int thin_status(struct dm_target *ti, status_type_t type,
static int thin_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
- dm_block_t blocks;
+ sector_t blocks;
struct thin_c *tc = ti->private;
+ struct pool *pool = tc->pool;
/*
* We can't call dm_pool_get_data_dev_size() since that blocks. So
* we follow a more convoluted path through to the pool's target.
*/
- if (!tc->pool->ti)
+ if (!pool->ti)
return 0; /* nothing is bound */
- blocks = tc->pool->ti->len >> tc->pool->block_shift;
+ blocks = pool->ti->len;
+ (void) sector_div(blocks, pool->sectors_per_block);
if (blocks)
- return fn(ti, tc->pool_dev, 0, tc->pool->sectors_per_block * blocks, data);
+ return fn(ti, tc->pool_dev, 0, pool->sectors_per_block * blocks, data);
return 0;
}
@@ -2786,7 +3054,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 1, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index fa365d39b612..254d19268ad2 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -515,7 +515,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio,
* Status: V (valid) or C (corruption found)
*/
static int verity_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
+ unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
unsigned sz = 0;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e24143cc2040..4e09b6ff5b49 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -968,22 +968,41 @@ static sector_t max_io_len_target_boundary(sector_t sector, struct dm_target *ti
static sector_t max_io_len(sector_t sector, struct dm_target *ti)
{
sector_t len = max_io_len_target_boundary(sector, ti);
+ sector_t offset, max_len;
/*
- * Does the target need to split even further ?
+ * Does the target need to split even further?
*/
- if (ti->split_io) {
- sector_t boundary;
- sector_t offset = dm_target_offset(ti, sector);
- boundary = ((offset + ti->split_io) & ~(ti->split_io - 1))
- - offset;
- if (len > boundary)
- len = boundary;
+ if (ti->max_io_len) {
+ offset = dm_target_offset(ti, sector);
+ if (unlikely(ti->max_io_len & (ti->max_io_len - 1)))
+ max_len = sector_div(offset, ti->max_io_len);
+ else
+ max_len = offset & (ti->max_io_len - 1);
+ max_len = ti->max_io_len - max_len;
+
+ if (len > max_len)
+ len = max_len;
}
return len;
}
+int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
+{
+ if (len > UINT_MAX) {
+ DMERR("Specified maximum size of target IO (%llu) exceeds limit (%u)",
+ (unsigned long long)len, UINT_MAX);
+ ti->error = "Maximum size of target IO is too large";
+ return -EINVAL;
+ }
+
+ ti->max_io_len = (uint32_t) len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_set_target_max_io_len);
+
static void __map_bio(struct dm_target *ti, struct bio *clone,
struct dm_target_io *tio)
{
@@ -1196,7 +1215,10 @@ static int __clone_and_map_discard(struct clone_info *ci)
if (!ti->num_discard_requests)
return -EOPNOTSUPP;
- len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+ if (!ti->split_discard_requests)
+ len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+ else
+ len = min(ci->sector_count, max_io_len(ci->sector, ti));
__issue_target_requests(ci, ti, ti->num_discard_requests, len);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index b7dacd59d8d7..52eef493d266 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -23,6 +23,11 @@
#define DM_SUSPEND_NOFLUSH_FLAG (1 << 1)
/*
+ * Status feature flags
+ */
+#define DM_STATUS_NOFLUSH_FLAG (1 << 0)
+
+/*
* Type of table and mapped_device's mempool
*/
#define DM_TYPE_NONE 0
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d5ab4493c8be..fcd098794d37 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -498,61 +498,13 @@ void md_flush_request(struct mddev *mddev, struct bio *bio)
}
EXPORT_SYMBOL(md_flush_request);
-/* Support for plugging.
- * This mirrors the plugging support in request_queue, but does not
- * require having a whole queue or request structures.
- * We allocate an md_plug_cb for each md device and each thread it gets
- * plugged on. This links tot the private plug_handle structure in the
- * personality data where we keep a count of the number of outstanding
- * plugs so other code can see if a plug is active.
- */
-struct md_plug_cb {
- struct blk_plug_cb cb;
- struct mddev *mddev;
-};
-
-static void plugger_unplug(struct blk_plug_cb *cb)
-{
- struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb);
- if (atomic_dec_and_test(&mdcb->mddev->plug_cnt))
- md_wakeup_thread(mdcb->mddev->thread);
- kfree(mdcb);
-}
-
-/* Check that an unplug wakeup will come shortly.
- * If not, wakeup the md thread immediately
- */
-int mddev_check_plugged(struct mddev *mddev)
+void md_unplug(struct blk_plug_cb *cb, bool from_schedule)
{
- struct blk_plug *plug = current->plug;
- struct md_plug_cb *mdcb;
-
- if (!plug)
- return 0;
-
- list_for_each_entry(mdcb, &plug->cb_list, cb.list) {
- if (mdcb->cb.callback == plugger_unplug &&
- mdcb->mddev == mddev) {
- /* Already on the list, move to top */
- if (mdcb != list_first_entry(&plug->cb_list,
- struct md_plug_cb,
- cb.list))
- list_move(&mdcb->cb.list, &plug->cb_list);
- return 1;
- }
- }
- /* Not currently on the callback list */
- mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC);
- if (!mdcb)
- return 0;
-
- mdcb->mddev = mddev;
- mdcb->cb.callback = plugger_unplug;
- atomic_inc(&mddev->plug_cnt);
- list_add(&mdcb->cb.list, &plug->cb_list);
- return 1;
+ struct mddev *mddev = cb->data;
+ md_wakeup_thread(mddev->thread);
+ kfree(cb);
}
-EXPORT_SYMBOL_GPL(mddev_check_plugged);
+EXPORT_SYMBOL(md_unplug);
static inline struct mddev *mddev_get(struct mddev *mddev)
{
@@ -602,7 +554,6 @@ void mddev_init(struct mddev *mddev)
atomic_set(&mddev->active, 1);
atomic_set(&mddev->openers, 0);
atomic_set(&mddev->active_io, 0);
- atomic_set(&mddev->plug_cnt, 0);
spin_lock_init(&mddev->write_lock);
atomic_set(&mddev->flush_pending, 0);
init_waitqueue_head(&mddev->sb_wait);
@@ -3942,17 +3893,13 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
break;
case clear:
/* stopping an active array */
- if (atomic_read(&mddev->openers) > 0)
- return -EBUSY;
err = do_md_stop(mddev, 0, NULL);
break;
case inactive:
/* stopping an active array */
- if (mddev->pers) {
- if (atomic_read(&mddev->openers) > 0)
- return -EBUSY;
+ if (mddev->pers)
err = do_md_stop(mddev, 2, NULL);
- } else
+ else
err = 0; /* already inactive */
break;
case suspended:
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 7b4a3c318cae..f385b038589d 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -266,9 +266,6 @@ struct mddev {
int new_chunk_sectors;
int reshape_backwards;
- atomic_t plug_cnt; /* If device is expecting
- * more bios soon.
- */
struct md_thread *thread; /* management thread */
struct md_thread *sync_thread; /* doing resync or reconstruct */
sector_t curr_resync; /* last block scheduled */
@@ -630,6 +627,12 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
struct mddev *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
struct mddev *mddev);
-extern int mddev_check_plugged(struct mddev *mddev);
extern void md_trim_bio(struct bio *bio, int offset, int size);
+
+extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule);
+static inline int mddev_check_plugged(struct mddev *mddev)
+{
+ return !!blk_check_plugged(md_unplug, mddev,
+ sizeof(struct blk_plug_cb));
+}
#endif /* _MD_MD_H */
diff --git a/drivers/md/persistent-data/Makefile b/drivers/md/persistent-data/Makefile
index cfa95f662230..d8e7cb767c1e 100644
--- a/drivers/md/persistent-data/Makefile
+++ b/drivers/md/persistent-data/Makefile
@@ -1,7 +1,6 @@
obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o
dm-persistent-data-objs := \
dm-block-manager.o \
- dm-space-map-checker.o \
dm-space-map-common.o \
dm-space-map-disk.o \
dm-space-map-metadata.o \
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 0317ecdc6e53..5ba277768d99 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -325,11 +325,6 @@ static struct dm_buffer *to_buffer(struct dm_block *b)
return (struct dm_buffer *) b;
}
-static struct dm_bufio_client *to_bufio(struct dm_block_manager *bm)
-{
- return (struct dm_bufio_client *) bm;
-}
-
dm_block_t dm_block_location(struct dm_block *b)
{
return dm_bufio_get_block_number(to_buffer(b));
@@ -367,34 +362,60 @@ static void dm_block_manager_write_callback(struct dm_buffer *buf)
/*----------------------------------------------------------------
* Public interface
*--------------------------------------------------------------*/
+struct dm_block_manager {
+ struct dm_bufio_client *bufio;
+ bool read_only:1;
+};
+
struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
unsigned block_size,
unsigned cache_size,
unsigned max_held_per_thread)
{
- return (struct dm_block_manager *)
- dm_bufio_client_create(bdev, block_size, max_held_per_thread,
- sizeof(struct buffer_aux),
- dm_block_manager_alloc_callback,
- dm_block_manager_write_callback);
+ int r;
+ struct dm_block_manager *bm;
+
+ bm = kmalloc(sizeof(*bm), GFP_KERNEL);
+ if (!bm) {
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
+ sizeof(struct buffer_aux),
+ dm_block_manager_alloc_callback,
+ dm_block_manager_write_callback);
+ if (IS_ERR(bm->bufio)) {
+ r = PTR_ERR(bm->bufio);
+ kfree(bm);
+ goto bad;
+ }
+
+ bm->read_only = false;
+
+ return bm;
+
+bad:
+ return ERR_PTR(r);
}
EXPORT_SYMBOL_GPL(dm_block_manager_create);
void dm_block_manager_destroy(struct dm_block_manager *bm)
{
- return dm_bufio_client_destroy(to_bufio(bm));
+ dm_bufio_client_destroy(bm->bufio);
+ kfree(bm);
}
EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
unsigned dm_bm_block_size(struct dm_block_manager *bm)
{
- return dm_bufio_get_block_size(to_bufio(bm));
+ return dm_bufio_get_block_size(bm->bufio);
}
EXPORT_SYMBOL_GPL(dm_bm_block_size);
dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
{
- return dm_bufio_get_device_size(to_bufio(bm));
+ return dm_bufio_get_device_size(bm->bufio);
}
static int dm_bm_validate_buffer(struct dm_block_manager *bm,
@@ -406,7 +427,7 @@ static int dm_bm_validate_buffer(struct dm_block_manager *bm,
int r;
if (!v)
return 0;
- r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(to_bufio(bm)));
+ r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(bm->bufio));
if (unlikely(r))
return r;
aux->validator = v;
@@ -430,7 +451,7 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
void *p;
int r;
- p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
if (unlikely(IS_ERR(p)))
return PTR_ERR(p);
@@ -463,7 +484,10 @@ int dm_bm_write_lock(struct dm_block_manager *bm,
void *p;
int r;
- p = dm_bufio_read(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (bm->read_only)
+ return -EPERM;
+
+ p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
if (unlikely(IS_ERR(p)))
return PTR_ERR(p);
@@ -496,7 +520,7 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm,
void *p;
int r;
- p = dm_bufio_get(to_bufio(bm), b, (struct dm_buffer **) result);
+ p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result);
if (unlikely(IS_ERR(p)))
return PTR_ERR(p);
if (unlikely(!p))
@@ -529,7 +553,10 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
struct buffer_aux *aux;
void *p;
- p = dm_bufio_new(to_bufio(bm), b, (struct dm_buffer **) result);
+ if (bm->read_only)
+ return -EPERM;
+
+ p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result);
if (unlikely(IS_ERR(p)))
return PTR_ERR(p);
@@ -547,6 +574,7 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
return 0;
}
+EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero);
int dm_bm_unlock(struct dm_block *b)
{
@@ -565,45 +593,30 @@ int dm_bm_unlock(struct dm_block *b)
}
EXPORT_SYMBOL_GPL(dm_bm_unlock);
-int dm_bm_unlock_move(struct dm_block *b, dm_block_t n)
-{
- struct buffer_aux *aux;
-
- aux = dm_bufio_get_aux_data(to_buffer(b));
-
- if (aux->write_locked) {
- dm_bufio_mark_buffer_dirty(to_buffer(b));
- bl_up_write(&aux->lock);
- } else
- bl_up_read(&aux->lock);
-
- dm_bufio_release_move(to_buffer(b), n);
- return 0;
-}
-
int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
struct dm_block *superblock)
{
int r;
- r = dm_bufio_write_dirty_buffers(to_bufio(bm));
- if (unlikely(r))
- return r;
- r = dm_bufio_issue_flush(to_bufio(bm));
- if (unlikely(r))
+ if (bm->read_only)
+ return -EPERM;
+
+ r = dm_bufio_write_dirty_buffers(bm->bufio);
+ if (unlikely(r)) {
+ dm_bm_unlock(superblock);
return r;
+ }
dm_bm_unlock(superblock);
- r = dm_bufio_write_dirty_buffers(to_bufio(bm));
- if (unlikely(r))
- return r;
- r = dm_bufio_issue_flush(to_bufio(bm));
- if (unlikely(r))
- return r;
+ return dm_bufio_write_dirty_buffers(bm->bufio);
+}
- return 0;
+void dm_bm_set_read_only(struct dm_block_manager *bm)
+{
+ bm->read_only = true;
}
+EXPORT_SYMBOL_GPL(dm_bm_set_read_only);
u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
{
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
index 924833d2dfa6..be5bff61be28 100644
--- a/drivers/md/persistent-data/dm-block-manager.h
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -97,14 +97,6 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
int dm_bm_unlock(struct dm_block *b);
/*
- * An optimisation; we often want to copy a block's contents to a new
- * block. eg, as part of the shadowing operation. It's far better for
- * bufio to do this move behind the scenes than hold 2 locks and memcpy the
- * data.
- */
-int dm_bm_unlock_move(struct dm_block *b, dm_block_t n);
-
-/*
* It's a common idiom to have a superblock that should be committed last.
*
* @superblock should be write-locked on entry. It will be unlocked during
@@ -116,6 +108,19 @@ int dm_bm_unlock_move(struct dm_block *b, dm_block_t n);
int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
struct dm_block *superblock);
+/*
+ * Switches the bm to a read only mode. Once read-only mode
+ * has been entered the following functions will return -EPERM.
+ *
+ * dm_bm_write_lock
+ * dm_bm_write_lock_zero
+ * dm_bm_flush_and_unlock
+ *
+ * Additionally you should not use dm_bm_unlock_move, however no error will
+ * be returned if you do.
+ */
+void dm_bm_set_read_only(struct dm_block_manager *bm);
+
u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c
deleted file mode 100644
index fc90c11620ad..000000000000
--- a/drivers/md/persistent-data/dm-space-map-checker.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This file is released under the GPL.
- */
-
-#include "dm-space-map-checker.h"
-
-#include <linux/device-mapper.h>
-#include <linux/export.h>
-#include <linux/vmalloc.h>
-
-#ifdef CONFIG_DM_DEBUG_SPACE_MAPS
-
-#define DM_MSG_PREFIX "space map checker"
-
-/*----------------------------------------------------------------*/
-
-struct count_array {
- dm_block_t nr;
- dm_block_t nr_free;
-
- uint32_t *counts;
-};
-
-static int ca_get_count(struct count_array *ca, dm_block_t b, uint32_t *count)
-{
- if (b >= ca->nr)
- return -EINVAL;
-
- *count = ca->counts[b];
- return 0;
-}
-
-static int ca_count_more_than_one(struct count_array *ca, dm_block_t b, int *r)
-{
- if (b >= ca->nr)
- return -EINVAL;
-
- *r = ca->counts[b] > 1;
- return 0;
-}
-
-static int ca_set_count(struct count_array *ca, dm_block_t b, uint32_t count)
-{
- uint32_t old_count;
-
- if (b >= ca->nr)
- return -EINVAL;
-
- old_count = ca->counts[b];
-
- if (!count && old_count)
- ca->nr_free++;
-
- else if (count && !old_count)
- ca->nr_free--;
-
- ca->counts[b] = count;
- return 0;
-}
-
-static int ca_inc_block(struct count_array *ca, dm_block_t b)
-{
- if (b >= ca->nr)
- return -EINVAL;
-
- ca_set_count(ca, b, ca->counts[b] + 1);
- return 0;
-}
-
-static int ca_dec_block(struct count_array *ca, dm_block_t b)
-{
- if (b >= ca->nr)
- return -EINVAL;
-
- BUG_ON(ca->counts[b] == 0);
- ca_set_count(ca, b, ca->counts[b] - 1);
- return 0;
-}
-
-static int ca_create(struct count_array *ca, struct dm_space_map *sm)
-{
- int r;
- dm_block_t nr_blocks;
-
- r = dm_sm_get_nr_blocks(sm, &nr_blocks);
- if (r)
- return r;
-
- ca->nr = nr_blocks;
- ca->nr_free = nr_blocks;
-
- if (!nr_blocks)
- ca->counts = NULL;
- else {
- ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks);
- if (!ca->counts)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void ca_destroy(struct count_array *ca)
-{
- vfree(ca->counts);
-}
-
-static int ca_load(struct count_array *ca, struct dm_space_map *sm)
-{
- int r;
- uint32_t count;
- dm_block_t nr_blocks, i;
-
- r = dm_sm_get_nr_blocks(sm, &nr_blocks);
- if (r)
- return r;
-
- BUG_ON(ca->nr != nr_blocks);
-
- DMWARN("Loading debug space map from disk. This may take some time");
- for (i = 0; i < nr_blocks; i++) {
- r = dm_sm_get_count(sm, i, &count);
- if (r) {
- DMERR("load failed");
- return r;
- }
-
- ca_set_count(ca, i, count);
- }
- DMWARN("Load complete");
-
- return 0;
-}
-
-static int ca_extend(struct count_array *ca, dm_block_t extra_blocks)
-{
- dm_block_t nr_blocks = ca->nr + extra_blocks;
- uint32_t *counts = vzalloc(sizeof(*counts) * nr_blocks);
- if (!counts)
- return -ENOMEM;
-
- if (ca->counts) {
- memcpy(counts, ca->counts, sizeof(*counts) * ca->nr);
- ca_destroy(ca);
- }
- ca->nr = nr_blocks;
- ca->nr_free += extra_blocks;
- ca->counts = counts;
- return 0;
-}
-
-static int ca_commit(struct count_array *old, struct count_array *new)
-{
- if (old->nr != new->nr) {
- BUG_ON(old->nr > new->nr);
- ca_extend(old, new->nr - old->nr);
- }
-
- BUG_ON(old->nr != new->nr);
- old->nr_free = new->nr_free;
- memcpy(old->counts, new->counts, sizeof(*old->counts) * old->nr);
- return 0;
-}
-
-/*----------------------------------------------------------------*/
-
-struct sm_checker {
- struct dm_space_map sm;
-
- struct count_array old_counts;
- struct count_array counts;
-
- struct dm_space_map *real_sm;
-};
-
-static void sm_checker_destroy(struct dm_space_map *sm)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
-
- dm_sm_destroy(smc->real_sm);
- ca_destroy(&smc->old_counts);
- ca_destroy(&smc->counts);
- kfree(smc);
-}
-
-static int sm_checker_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_get_nr_blocks(smc->real_sm, count);
- if (!r)
- BUG_ON(smc->old_counts.nr != *count);
- return r;
-}
-
-static int sm_checker_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_get_nr_free(smc->real_sm, count);
- if (!r) {
- /*
- * Slow, but we know it's correct.
- */
- dm_block_t b, n = 0;
- for (b = 0; b < smc->old_counts.nr; b++)
- if (smc->old_counts.counts[b] == 0 &&
- smc->counts.counts[b] == 0)
- n++;
-
- if (n != *count)
- DMERR("free block counts differ, checker %u, sm-disk:%u",
- (unsigned) n, (unsigned) *count);
- }
- return r;
-}
-
-static int sm_checker_new_block(struct dm_space_map *sm, dm_block_t *b)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_new_block(smc->real_sm, b);
-
- if (!r) {
- BUG_ON(*b >= smc->old_counts.nr);
- BUG_ON(smc->old_counts.counts[*b] != 0);
- BUG_ON(*b >= smc->counts.nr);
- BUG_ON(smc->counts.counts[*b] != 0);
- ca_set_count(&smc->counts, *b, 1);
- }
-
- return r;
-}
-
-static int sm_checker_inc_block(struct dm_space_map *sm, dm_block_t b)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_inc_block(smc->real_sm, b);
- int r2 = ca_inc_block(&smc->counts, b);
- BUG_ON(r != r2);
- return r;
-}
-
-static int sm_checker_dec_block(struct dm_space_map *sm, dm_block_t b)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_dec_block(smc->real_sm, b);
- int r2 = ca_dec_block(&smc->counts, b);
- BUG_ON(r != r2);
- return r;
-}
-
-static int sm_checker_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- uint32_t result2 = 0;
- int r = dm_sm_get_count(smc->real_sm, b, result);
- int r2 = ca_get_count(&smc->counts, b, &result2);
-
- BUG_ON(r != r2);
- if (!r)
- BUG_ON(*result != result2);
- return r;
-}
-
-static int sm_checker_count_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int result2 = 0;
- int r = dm_sm_count_is_more_than_one(smc->real_sm, b, result);
- int r2 = ca_count_more_than_one(&smc->counts, b, &result2);
-
- BUG_ON(r != r2);
- if (!r)
- BUG_ON(!(*result) && result2);
- return r;
-}
-
-static int sm_checker_set_count(struct dm_space_map *sm, dm_block_t b, uint32_t count)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- uint32_t old_rc;
- int r = dm_sm_set_count(smc->real_sm, b, count);
- int r2;
-
- BUG_ON(b >= smc->counts.nr);
- old_rc = smc->counts.counts[b];
- r2 = ca_set_count(&smc->counts, b, count);
- BUG_ON(r != r2);
-
- return r;
-}
-
-static int sm_checker_commit(struct dm_space_map *sm)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r;
-
- r = dm_sm_commit(smc->real_sm);
- if (r)
- return r;
-
- r = ca_commit(&smc->old_counts, &smc->counts);
- if (r)
- return r;
-
- return 0;
-}
-
-static int sm_checker_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- int r = dm_sm_extend(smc->real_sm, extra_blocks);
- if (r)
- return r;
-
- return ca_extend(&smc->counts, extra_blocks);
-}
-
-static int sm_checker_root_size(struct dm_space_map *sm, size_t *result)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- return dm_sm_root_size(smc->real_sm, result);
-}
-
-static int sm_checker_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
-{
- struct sm_checker *smc = container_of(sm, struct sm_checker, sm);
- return dm_sm_copy_root(smc->real_sm, copy_to_here_le, len);
-}
-
-/*----------------------------------------------------------------*/
-
-static struct dm_space_map ops_ = {
- .destroy = sm_checker_destroy,
- .get_nr_blocks = sm_checker_get_nr_blocks,
- .get_nr_free = sm_checker_get_nr_free,
- .inc_block = sm_checker_inc_block,
- .dec_block = sm_checker_dec_block,
- .new_block = sm_checker_new_block,
- .get_count = sm_checker_get_count,
- .count_is_more_than_one = sm_checker_count_more_than_one,
- .set_count = sm_checker_set_count,
- .commit = sm_checker_commit,
- .extend = sm_checker_extend,
- .root_size = sm_checker_root_size,
- .copy_root = sm_checker_copy_root
-};
-
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
-{
- int r;
- struct sm_checker *smc;
-
- if (IS_ERR_OR_NULL(sm))
- return ERR_PTR(-EINVAL);
-
- smc = kmalloc(sizeof(*smc), GFP_KERNEL);
- if (!smc)
- return ERR_PTR(-ENOMEM);
-
- memcpy(&smc->sm, &ops_, sizeof(smc->sm));
- r = ca_create(&smc->old_counts, sm);
- if (r) {
- kfree(smc);
- return ERR_PTR(r);
- }
-
- r = ca_create(&smc->counts, sm);
- if (r) {
- ca_destroy(&smc->old_counts);
- kfree(smc);
- return ERR_PTR(r);
- }
-
- smc->real_sm = sm;
-
- r = ca_load(&smc->counts, sm);
- if (r) {
- ca_destroy(&smc->counts);
- ca_destroy(&smc->old_counts);
- kfree(smc);
- return ERR_PTR(r);
- }
-
- r = ca_commit(&smc->old_counts, &smc->counts);
- if (r) {
- ca_destroy(&smc->counts);
- ca_destroy(&smc->old_counts);
- kfree(smc);
- return ERR_PTR(r);
- }
-
- return &smc->sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create);
-
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
-{
- int r;
- struct sm_checker *smc;
-
- if (IS_ERR_OR_NULL(sm))
- return ERR_PTR(-EINVAL);
-
- smc = kmalloc(sizeof(*smc), GFP_KERNEL);
- if (!smc)
- return ERR_PTR(-ENOMEM);
-
- memcpy(&smc->sm, &ops_, sizeof(smc->sm));
- r = ca_create(&smc->old_counts, sm);
- if (r) {
- kfree(smc);
- return ERR_PTR(r);
- }
-
- r = ca_create(&smc->counts, sm);
- if (r) {
- ca_destroy(&smc->old_counts);
- kfree(smc);
- return ERR_PTR(r);
- }
-
- smc->real_sm = sm;
- return &smc->sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
-
-/*----------------------------------------------------------------*/
-
-#else
-
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm)
-{
- return sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create);
-
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm)
-{
- return sm;
-}
-EXPORT_SYMBOL_GPL(dm_sm_checker_create_fresh);
-
-/*----------------------------------------------------------------*/
-
-#endif
diff --git a/drivers/md/persistent-data/dm-space-map-checker.h b/drivers/md/persistent-data/dm-space-map-checker.h
deleted file mode 100644
index 444dccf6688c..000000000000
--- a/drivers/md/persistent-data/dm-space-map-checker.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This file is released under the GPL.
- */
-
-#ifndef SNAPSHOTS_SPACE_MAP_CHECKER_H
-#define SNAPSHOTS_SPACE_MAP_CHECKER_H
-
-#include "dm-space-map.h"
-
-/*----------------------------------------------------------------*/
-
-/*
- * This space map wraps a real on-disk space map, and verifies all of its
- * operations. It uses a lot of memory, so only use if you have a specific
- * problem that you're debugging.
- *
- * Ownership of @sm passes.
- */
-struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm);
-struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm);
-
-/*----------------------------------------------------------------*/
-
-#endif
diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c
index ff3beed6ad2d..d77602d63c83 100644
--- a/drivers/md/persistent-data/dm-space-map-common.c
+++ b/drivers/md/persistent-data/dm-space-map-common.c
@@ -224,6 +224,7 @@ static int sm_ll_init(struct ll_disk *ll, struct dm_transaction_manager *tm)
ll->nr_blocks = 0;
ll->bitmap_root = 0;
ll->ref_count_root = 0;
+ ll->bitmap_index_changed = false;
return 0;
}
@@ -476,7 +477,15 @@ int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev)
int sm_ll_commit(struct ll_disk *ll)
{
- return ll->commit(ll);
+ int r = 0;
+
+ if (ll->bitmap_index_changed) {
+ r = ll->commit(ll);
+ if (!r)
+ ll->bitmap_index_changed = false;
+ }
+
+ return r;
}
/*----------------------------------------------------------------*/
@@ -491,6 +500,7 @@ static int metadata_ll_load_ie(struct ll_disk *ll, dm_block_t index,
static int metadata_ll_save_ie(struct ll_disk *ll, dm_block_t index,
struct disk_index_entry *ie)
{
+ ll->bitmap_index_changed = true;
memcpy(ll->mi_le.index + index, ie, sizeof(*ie));
return 0;
}
diff --git a/drivers/md/persistent-data/dm-space-map-common.h b/drivers/md/persistent-data/dm-space-map-common.h
index 8f220821a9a9..b3078d5eda0c 100644
--- a/drivers/md/persistent-data/dm-space-map-common.h
+++ b/drivers/md/persistent-data/dm-space-map-common.h
@@ -78,6 +78,7 @@ struct ll_disk {
open_index_fn open_index;
max_index_entries_fn max_entries;
commit_fn commit;
+ bool bitmap_index_changed:1;
};
struct disk_sm_root {
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
index 3d0ed5332883..f6d29e614ab7 100644
--- a/drivers/md/persistent-data/dm-space-map-disk.c
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -4,7 +4,6 @@
* This file is released under the GPL.
*/
-#include "dm-space-map-checker.h"
#include "dm-space-map-common.h"
#include "dm-space-map-disk.h"
#include "dm-space-map.h"
@@ -252,9 +251,8 @@ static struct dm_space_map ops = {
.copy_root = sm_disk_copy_root
};
-static struct dm_space_map *dm_sm_disk_create_real(
- struct dm_transaction_manager *tm,
- dm_block_t nr_blocks)
+struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
+ dm_block_t nr_blocks)
{
int r;
struct sm_disk *smd;
@@ -285,27 +283,10 @@ bad:
kfree(smd);
return ERR_PTR(r);
}
-
-struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
- dm_block_t nr_blocks)
-{
- struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks);
- struct dm_space_map *smc;
-
- if (IS_ERR_OR_NULL(sm))
- return sm;
-
- smc = dm_sm_checker_create_fresh(sm);
- if (IS_ERR(smc))
- dm_sm_destroy(sm);
-
- return smc;
-}
EXPORT_SYMBOL_GPL(dm_sm_disk_create);
-static struct dm_space_map *dm_sm_disk_open_real(
- struct dm_transaction_manager *tm,
- void *root_le, size_t len)
+struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
+ void *root_le, size_t len)
{
int r;
struct sm_disk *smd;
@@ -332,13 +313,6 @@ bad:
kfree(smd);
return ERR_PTR(r);
}
-
-struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
- void *root_le, size_t len)
-{
- return dm_sm_checker_create(
- dm_sm_disk_open_real(tm, root_le, len));
-}
EXPORT_SYMBOL_GPL(dm_sm_disk_open);
/*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
index e5604b32d91f..d247a35da3c6 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.c
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -5,7 +5,6 @@
*/
#include "dm-transaction-manager.h"
#include "dm-space-map.h"
-#include "dm-space-map-checker.h"
#include "dm-space-map-disk.h"
#include "dm-space-map-metadata.h"
#include "dm-persistent-data-internal.h"
@@ -220,13 +219,24 @@ static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
if (r < 0)
return r;
- r = dm_bm_unlock_move(orig_block, new);
- if (r < 0) {
+ /*
+ * It would be tempting to use dm_bm_unlock_move here, but some
+ * code, such as the space maps, keeps using the old data structures
+ * secure in the knowledge they won't be changed until the next
+ * transaction. Using unlock_move would force a synchronous read
+ * since the old block would no longer be in the cache.
+ */
+ r = dm_bm_write_lock_zero(tm->bm, new, v, result);
+ if (r) {
dm_bm_unlock(orig_block);
return r;
}
- return dm_bm_write_lock(tm->bm, new, v, result);
+ memcpy(dm_block_data(*result), dm_block_data(orig_block),
+ dm_bm_block_size(tm->bm));
+
+ dm_bm_unlock(orig_block);
+ return r;
}
int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
@@ -311,98 +321,61 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
static int dm_tm_create_internal(struct dm_block_manager *bm,
dm_block_t sb_location,
- struct dm_block_validator *sb_validator,
- size_t root_offset, size_t root_max_len,
struct dm_transaction_manager **tm,
struct dm_space_map **sm,
- struct dm_block **sblock,
- int create)
+ int create,
+ void *sm_root, size_t sm_len)
{
int r;
- struct dm_space_map *inner;
- inner = dm_sm_metadata_init();
- if (IS_ERR(inner))
- return PTR_ERR(inner);
+ *sm = dm_sm_metadata_init();
+ if (IS_ERR(*sm))
+ return PTR_ERR(*sm);
- *tm = dm_tm_create(bm, inner);
+ *tm = dm_tm_create(bm, *sm);
if (IS_ERR(*tm)) {
- dm_sm_destroy(inner);
+ dm_sm_destroy(*sm);
return PTR_ERR(*tm);
}
if (create) {
- r = dm_bm_write_lock_zero(dm_tm_get_bm(*tm), sb_location,
- sb_validator, sblock);
- if (r < 0) {
- DMERR("couldn't lock superblock");
- goto bad1;
- }
-
- r = dm_sm_metadata_create(inner, *tm, dm_bm_nr_blocks(bm),
+ r = dm_sm_metadata_create(*sm, *tm, dm_bm_nr_blocks(bm),
sb_location);
if (r) {
DMERR("couldn't create metadata space map");
- goto bad2;
- }
-
- *sm = dm_sm_checker_create(inner);
- if (IS_ERR(*sm)) {
- r = PTR_ERR(*sm);
- goto bad2;
+ goto bad;
}
} else {
- r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location,
- sb_validator, sblock);
- if (r < 0) {
- DMERR("couldn't lock superblock");
- goto bad1;
- }
-
- r = dm_sm_metadata_open(inner, *tm,
- dm_block_data(*sblock) + root_offset,
- root_max_len);
+ r = dm_sm_metadata_open(*sm, *tm, sm_root, sm_len);
if (r) {
DMERR("couldn't open metadata space map");
- goto bad2;
- }
-
- *sm = dm_sm_checker_create(inner);
- if (IS_ERR(*sm)) {
- r = PTR_ERR(*sm);
- goto bad2;
+ goto bad;
}
}
return 0;
-bad2:
- dm_tm_unlock(*tm, *sblock);
-bad1:
+bad:
dm_tm_destroy(*tm);
- dm_sm_destroy(inner);
+ dm_sm_destroy(*sm);
return r;
}
int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
- struct dm_block_validator *sb_validator,
struct dm_transaction_manager **tm,
- struct dm_space_map **sm, struct dm_block **sblock)
+ struct dm_space_map **sm)
{
- return dm_tm_create_internal(bm, sb_location, sb_validator,
- 0, 0, tm, sm, sblock, 1);
+ return dm_tm_create_internal(bm, sb_location, tm, sm, 1, NULL, 0);
}
EXPORT_SYMBOL_GPL(dm_tm_create_with_sm);
int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
- struct dm_block_validator *sb_validator,
- size_t root_offset, size_t root_max_len,
+ void *sm_root, size_t root_len,
struct dm_transaction_manager **tm,
- struct dm_space_map **sm, struct dm_block **sblock)
+ struct dm_space_map **sm)
{
- return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset,
- root_max_len, tm, sm, sblock, 0);
+ return dm_tm_create_internal(bm, sb_location, tm, sm, 0, sm_root, root_len);
}
EXPORT_SYMBOL_GPL(dm_tm_open_with_sm);
diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h
index 6da784871db4..b5b139076ca5 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.h
+++ b/drivers/md/persistent-data/dm-transaction-manager.h
@@ -115,16 +115,17 @@ struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
*
* Returns a tm that has an open transaction to write the new disk sm.
* Caller should store the new sm root and commit.
+ *
+ * The superblock location is passed so the metadata space map knows it
+ * shouldn't be used.
*/
int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
- struct dm_block_validator *sb_validator,
struct dm_transaction_manager **tm,
- struct dm_space_map **sm, struct dm_block **sblock);
+ struct dm_space_map **sm);
int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
- struct dm_block_validator *sb_validator,
- size_t root_offset, size_t root_max_len,
+ void *sm_root, size_t root_len,
struct dm_transaction_manager **tm,
- struct dm_space_map **sm, struct dm_block **sblock);
+ struct dm_space_map **sm);
#endif /* _LINUX_DM_TRANSACTION_MANAGER_H */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index cacd008d6864..9f7f8bee8442 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -46,6 +46,20 @@
*/
#define NR_RAID1_BIOS 256
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error. To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
/* When there are this many requests queue to be written by
* the raid1 thread, we become 'congested' to provide back-pressure
* for writeback.
@@ -483,12 +497,14 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
const sector_t this_sector = r1_bio->sector;
int sectors;
int best_good_sectors;
- int start_disk;
- int best_disk;
- int i;
+ int best_disk, best_dist_disk, best_pending_disk;
+ int has_nonrot_disk;
+ int disk;
sector_t best_dist;
+ unsigned int min_pending;
struct md_rdev *rdev;
int choose_first;
+ int choose_next_idle;
rcu_read_lock();
/*
@@ -499,26 +515,26 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
retry:
sectors = r1_bio->sectors;
best_disk = -1;
+ best_dist_disk = -1;
best_dist = MaxSector;
+ best_pending_disk = -1;
+ min_pending = UINT_MAX;
best_good_sectors = 0;
+ has_nonrot_disk = 0;
+ choose_next_idle = 0;
if (conf->mddev->recovery_cp < MaxSector &&
- (this_sector + sectors >= conf->next_resync)) {
+ (this_sector + sectors >= conf->next_resync))
choose_first = 1;
- start_disk = 0;
- } else {
+ else
choose_first = 0;
- start_disk = conf->last_used;
- }
- for (i = 0 ; i < conf->raid_disks * 2 ; i++) {
+ for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) {
sector_t dist;
sector_t first_bad;
int bad_sectors;
-
- int disk = start_disk + i;
- if (disk >= conf->raid_disks * 2)
- disk -= conf->raid_disks * 2;
+ unsigned int pending;
+ bool nonrot;
rdev = rcu_dereference(conf->mirrors[disk].rdev);
if (r1_bio->bios[disk] == IO_BLOCKED
@@ -577,22 +593,77 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
} else
best_good_sectors = sectors;
+ nonrot = blk_queue_nonrot(bdev_get_queue(rdev->bdev));
+ has_nonrot_disk |= nonrot;
+ pending = atomic_read(&rdev->nr_pending);
dist = abs(this_sector - conf->mirrors[disk].head_position);
- if (choose_first
- /* Don't change to another disk for sequential reads */
- || conf->next_seq_sect == this_sector
- || dist == 0
- /* If device is idle, use it */
- || atomic_read(&rdev->nr_pending) == 0) {
+ if (choose_first) {
+ best_disk = disk;
+ break;
+ }
+ /* Don't change to another disk for sequential reads */
+ if (conf->mirrors[disk].next_seq_sect == this_sector
+ || dist == 0) {
+ int opt_iosize = bdev_io_opt(rdev->bdev) >> 9;
+ struct raid1_info *mirror = &conf->mirrors[disk];
+
+ best_disk = disk;
+ /*
+ * If buffered sequential IO size exceeds optimal
+ * iosize, check if there is idle disk. If yes, choose
+ * the idle disk. read_balance could already choose an
+ * idle disk before noticing it's a sequential IO in
+ * this disk. This doesn't matter because this disk
+ * will idle, next time it will be utilized after the
+ * first disk has IO size exceeds optimal iosize. In
+ * this way, iosize of the first disk will be optimal
+ * iosize at least. iosize of the second disk might be
+ * small, but not a big deal since when the second disk
+ * starts IO, the first disk is likely still busy.
+ */
+ if (nonrot && opt_iosize > 0 &&
+ mirror->seq_start != MaxSector &&
+ mirror->next_seq_sect > opt_iosize &&
+ mirror->next_seq_sect - opt_iosize >=
+ mirror->seq_start) {
+ choose_next_idle = 1;
+ continue;
+ }
+ break;
+ }
+ /* If device is idle, use it */
+ if (pending == 0) {
best_disk = disk;
break;
}
+
+ if (choose_next_idle)
+ continue;
+
+ if (min_pending > pending) {
+ min_pending = pending;
+ best_pending_disk = disk;
+ }
+
if (dist < best_dist) {
best_dist = dist;
- best_disk = disk;
+ best_dist_disk = disk;
}
}
+ /*
+ * If all disks are rotational, choose the closest disk. If any disk is
+ * non-rotational, choose the disk with less pending request even the
+ * disk is rotational, which might/might not be optimal for raids with
+ * mixed ratation/non-rotational disks depending on workload.
+ */
+ if (best_disk == -1) {
+ if (has_nonrot_disk)
+ best_disk = best_pending_disk;
+ else
+ best_disk = best_dist_disk;
+ }
+
if (best_disk >= 0) {
rdev = rcu_dereference(conf->mirrors[best_disk].rdev);
if (!rdev)
@@ -606,8 +677,11 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
goto retry;
}
sectors = best_good_sectors;
- conf->next_seq_sect = this_sector + sectors;
- conf->last_used = best_disk;
+
+ if (conf->mirrors[best_disk].next_seq_sect != this_sector)
+ conf->mirrors[best_disk].seq_start = this_sector;
+
+ conf->mirrors[best_disk].next_seq_sect = this_sector + sectors;
}
rcu_read_unlock();
*max_sectors = sectors;
@@ -873,7 +947,7 @@ do_sync_io:
static void make_request(struct mddev *mddev, struct bio * bio)
{
struct r1conf *conf = mddev->private;
- struct mirror_info *mirror;
+ struct raid1_info *mirror;
struct r1bio *r1_bio;
struct bio *read_bio;
int i, disks;
@@ -1364,7 +1438,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
struct r1conf *conf = mddev->private;
int err = -EEXIST;
int mirror = 0;
- struct mirror_info *p;
+ struct raid1_info *p;
int first = 0;
int last = conf->raid_disks - 1;
struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -1433,7 +1507,7 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct r1conf *conf = mddev->private;
int err = 0;
int number = rdev->raid_disk;
- struct mirror_info *p = conf->mirrors+ number;
+ struct raid1_info *p = conf->mirrors + number;
if (rdev != p->rdev)
p = conf->mirrors + conf->raid_disks + number;
@@ -2173,8 +2247,7 @@ static void raid1d(struct mddev *mddev)
blk_start_plug(&plug);
for (;;) {
- if (atomic_read(&mddev->plug_cnt) == 0)
- flush_pending_writes(conf);
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
@@ -2371,6 +2444,18 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
bio->bi_rw = READ;
bio->bi_end_io = end_sync_read;
read_targets++;
+ } else if (!test_bit(WriteErrorSeen, &rdev->flags) &&
+ test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
+ !test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
+ /*
+ * The device is suitable for reading (InSync),
+ * but has bad block(s) here. Let's try to correct them,
+ * if we are doing resync or repair. Otherwise, leave
+ * this device alone for this sync request.
+ */
+ bio->bi_rw = WRITE;
+ bio->bi_end_io = end_sync_write;
+ write_targets++;
}
}
if (bio->bi_end_io) {
@@ -2428,7 +2513,10 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
/* There is nowhere to write, so all non-sync
* drives must be failed - so we are finished
*/
- sector_t rv = max_sector - sector_nr;
+ sector_t rv;
+ if (min_bad > 0)
+ max_sector = sector_nr + min_bad;
+ rv = max_sector - sector_nr;
*skipped = 1;
put_buf(r1_bio);
return rv;
@@ -2521,7 +2609,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
{
struct r1conf *conf;
int i;
- struct mirror_info *disk;
+ struct raid1_info *disk;
struct md_rdev *rdev;
int err = -ENOMEM;
@@ -2529,7 +2617,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
if (!conf)
goto abort;
- conf->mirrors = kzalloc(sizeof(struct mirror_info)
+ conf->mirrors = kzalloc(sizeof(struct raid1_info)
* mddev->raid_disks * 2,
GFP_KERNEL);
if (!conf->mirrors)
@@ -2572,6 +2660,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
mddev->merge_check_needed = 1;
disk->head_position = 0;
+ disk->seq_start = MaxSector;
}
conf->raid_disks = mddev->raid_disks;
conf->mddev = mddev;
@@ -2585,7 +2674,6 @@ static struct r1conf *setup_conf(struct mddev *mddev)
conf->recovery_disabled = mddev->recovery_disabled - 1;
err = -EIO;
- conf->last_used = -1;
for (i = 0; i < conf->raid_disks * 2; i++) {
disk = conf->mirrors + i;
@@ -2611,19 +2699,9 @@ static struct r1conf *setup_conf(struct mddev *mddev)
if (disk->rdev &&
(disk->rdev->saved_raid_disk < 0))
conf->fullsync = 1;
- } else if (conf->last_used < 0)
- /*
- * The first working device is used as a
- * starting point to read balancing.
- */
- conf->last_used = i;
+ }
}
- if (conf->last_used < 0) {
- printk(KERN_ERR "md/raid1:%s: no operational mirrors\n",
- mdname(mddev));
- goto abort;
- }
err = -ENOMEM;
conf->thread = md_register_thread(raid1d, mddev, "raid1");
if (!conf->thread) {
@@ -2798,7 +2876,7 @@ static int raid1_reshape(struct mddev *mddev)
*/
mempool_t *newpool, *oldpool;
struct pool_info *newpoolinfo;
- struct mirror_info *newmirrors;
+ struct raid1_info *newmirrors;
struct r1conf *conf = mddev->private;
int cnt, raid_disks;
unsigned long flags;
@@ -2841,7 +2919,7 @@ static int raid1_reshape(struct mddev *mddev)
kfree(newpoolinfo);
return -ENOMEM;
}
- newmirrors = kzalloc(sizeof(struct mirror_info) * raid_disks * 2,
+ newmirrors = kzalloc(sizeof(struct raid1_info) * raid_disks * 2,
GFP_KERNEL);
if (!newmirrors) {
kfree(newpoolinfo);
@@ -2880,7 +2958,6 @@ static int raid1_reshape(struct mddev *mddev)
conf->raid_disks = mddev->raid_disks = raid_disks;
mddev->delta_disks = 0;
- conf->last_used = 0; /* just make sure it is in-range */
lower_barrier(conf);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index 80ded139314c..0ff3715fb7eb 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -1,9 +1,15 @@
#ifndef _RAID1_H
#define _RAID1_H
-struct mirror_info {
+struct raid1_info {
struct md_rdev *rdev;
sector_t head_position;
+
+ /* When choose the best device for a read (read_balance())
+ * we try to keep sequential reads one the same device
+ */
+ sector_t next_seq_sect;
+ sector_t seq_start;
};
/*
@@ -24,17 +30,11 @@ struct pool_info {
struct r1conf {
struct mddev *mddev;
- struct mirror_info *mirrors; /* twice 'raid_disks' to
+ struct raid1_info *mirrors; /* twice 'raid_disks' to
* allow for replacements.
*/
int raid_disks;
- /* When choose the best device for a read (read_balance())
- * we try to keep sequential reads one the same device
- * using 'last_used' and 'next_seq_sect'
- */
- int last_used;
- sector_t next_seq_sect;
/* During resync, read_balancing is only allowed on the part
* of the array that has been resynced. 'next_resync' tells us
* where that is.
@@ -135,20 +135,6 @@ struct r1bio {
/* DO NOT PUT ANY NEW FIELDS HERE - bios array is contiguously alloced*/
};
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error. To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio *)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context. So we record
- * the success by setting bios[n] to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
/* bits for r1bio.state */
#define R1BIO_Uptodate 0
#define R1BIO_IsSync 1
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8da6282254c3..de5ed6fd8806 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -60,7 +60,21 @@
*/
#define NR_RAID10_BIOS 256
-/* When there are this many requests queue to be written by
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error. To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
+/* When there are this many requests queued to be written by
* the raid10 thread, we become 'congested' to provide back-pressure
* for writeback.
*/
@@ -717,7 +731,7 @@ static struct md_rdev *read_balance(struct r10conf *conf,
int sectors = r10_bio->sectors;
int best_good_sectors;
sector_t new_distance, best_dist;
- struct md_rdev *rdev, *best_rdev;
+ struct md_rdev *best_rdev, *rdev = NULL;
int do_balance;
int best_slot;
struct geom *geo = &conf->geo;
@@ -839,9 +853,8 @@ retry:
return rdev;
}
-static int raid10_congested(void *data, int bits)
+int md_raid10_congested(struct mddev *mddev, int bits)
{
- struct mddev *mddev = data;
struct r10conf *conf = mddev->private;
int i, ret = 0;
@@ -849,8 +862,6 @@ static int raid10_congested(void *data, int bits)
conf->pending_count >= max_queued_requests)
return 1;
- if (mddev_congested(mddev, bits))
- return 1;
rcu_read_lock();
for (i = 0;
(i < conf->geo.raid_disks || i < conf->prev.raid_disks)
@@ -866,6 +877,15 @@ static int raid10_congested(void *data, int bits)
rcu_read_unlock();
return ret;
}
+EXPORT_SYMBOL_GPL(md_raid10_congested);
+
+static int raid10_congested(void *data, int bits)
+{
+ struct mddev *mddev = data;
+
+ return mddev_congested(mddev, bits) ||
+ md_raid10_congested(mddev, bits);
+}
static void flush_pending_writes(struct r10conf *conf)
{
@@ -1546,7 +1566,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
static void print_conf(struct r10conf *conf)
{
int i;
- struct mirror_info *tmp;
+ struct raid10_info *tmp;
printk(KERN_DEBUG "RAID10 conf printout:\n");
if (!conf) {
@@ -1580,7 +1600,7 @@ static int raid10_spare_active(struct mddev *mddev)
{
int i;
struct r10conf *conf = mddev->private;
- struct mirror_info *tmp;
+ struct raid10_info *tmp;
int count = 0;
unsigned long flags;
@@ -1655,7 +1675,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
else
mirror = first;
for ( ; mirror <= last ; mirror++) {
- struct mirror_info *p = &conf->mirrors[mirror];
+ struct raid10_info *p = &conf->mirrors[mirror];
if (p->recovery_disabled == mddev->recovery_disabled)
continue;
if (p->rdev) {
@@ -1709,7 +1729,7 @@ static int raid10_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
int err = 0;
int number = rdev->raid_disk;
struct md_rdev **rdevp;
- struct mirror_info *p = conf->mirrors + number;
+ struct raid10_info *p = conf->mirrors + number;
print_conf(conf);
if (rdev == p->rdev)
@@ -2660,8 +2680,7 @@ static void raid10d(struct mddev *mddev)
blk_start_plug(&plug);
for (;;) {
- if (atomic_read(&mddev->plug_cnt) == 0)
- flush_pending_writes(conf);
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
@@ -2876,7 +2895,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
sector_t sect;
int must_sync;
int any_working;
- struct mirror_info *mirror = &conf->mirrors[i];
+ struct raid10_info *mirror = &conf->mirrors[i];
if ((mirror->rdev == NULL ||
test_bit(In_sync, &mirror->rdev->flags))
@@ -3388,7 +3407,7 @@ static struct r10conf *setup_conf(struct mddev *mddev)
goto out;
/* FIXME calc properly */
- conf->mirrors = kzalloc(sizeof(struct mirror_info)*(mddev->raid_disks +
+ conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks +
max(0,mddev->delta_disks)),
GFP_KERNEL);
if (!conf->mirrors)
@@ -3452,7 +3471,7 @@ static int run(struct mddev *mddev)
{
struct r10conf *conf;
int i, disk_idx, chunk_size;
- struct mirror_info *disk;
+ struct raid10_info *disk;
struct md_rdev *rdev;
sector_t size;
sector_t min_offset_diff = 0;
@@ -3472,12 +3491,14 @@ static int run(struct mddev *mddev)
conf->thread = NULL;
chunk_size = mddev->chunk_sectors << 9;
- blk_queue_io_min(mddev->queue, chunk_size);
- if (conf->geo.raid_disks % conf->geo.near_copies)
- blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
- else
- blk_queue_io_opt(mddev->queue, chunk_size *
- (conf->geo.raid_disks / conf->geo.near_copies));
+ if (mddev->queue) {
+ blk_queue_io_min(mddev->queue, chunk_size);
+ if (conf->geo.raid_disks % conf->geo.near_copies)
+ blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
+ else
+ blk_queue_io_opt(mddev->queue, chunk_size *
+ (conf->geo.raid_disks / conf->geo.near_copies));
+ }
rdev_for_each(rdev, mddev) {
long long diff;
@@ -3511,8 +3532,9 @@ static int run(struct mddev *mddev)
if (first || diff < min_offset_diff)
min_offset_diff = diff;
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
+ if (mddev->gendisk)
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
disk->head_position = 0;
}
@@ -3575,22 +3597,22 @@ static int run(struct mddev *mddev)
md_set_array_sectors(mddev, size);
mddev->resync_max_sectors = size;
- mddev->queue->backing_dev_info.congested_fn = raid10_congested;
- mddev->queue->backing_dev_info.congested_data = mddev;
-
- /* Calculate max read-ahead size.
- * We need to readahead at least twice a whole stripe....
- * maybe...
- */
- {
+ if (mddev->queue) {
int stripe = conf->geo.raid_disks *
((mddev->chunk_sectors << 9) / PAGE_SIZE);
+ mddev->queue->backing_dev_info.congested_fn = raid10_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
+
+ /* Calculate max read-ahead size.
+ * We need to readahead at least twice a whole stripe....
+ * maybe...
+ */
stripe /= conf->geo.near_copies;
if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
+ blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
}
- blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
if (md_integrity_register(mddev))
goto out_free_conf;
@@ -3641,7 +3663,10 @@ static int stop(struct mddev *mddev)
lower_barrier(conf);
md_unregister_thread(&mddev->thread);
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
+ if (mddev->queue)
+ /* the unplug fn references 'conf'*/
+ blk_sync_queue(mddev->queue);
+
if (conf->r10bio_pool)
mempool_destroy(conf->r10bio_pool);
kfree(conf->mirrors);
@@ -3805,7 +3830,7 @@ static int raid10_check_reshape(struct mddev *mddev)
if (mddev->delta_disks > 0) {
/* allocate new 'mirrors' list */
conf->mirrors_new = kzalloc(
- sizeof(struct mirror_info)
+ sizeof(struct raid10_info)
*(mddev->raid_disks +
mddev->delta_disks),
GFP_KERNEL);
@@ -3930,7 +3955,7 @@ static int raid10_start_reshape(struct mddev *mddev)
spin_lock_irq(&conf->device_lock);
if (conf->mirrors_new) {
memcpy(conf->mirrors_new, conf->mirrors,
- sizeof(struct mirror_info)*conf->prev.raid_disks);
+ sizeof(struct raid10_info)*conf->prev.raid_disks);
smp_mb();
kfree(conf->mirrors_old); /* FIXME and elsewhere */
conf->mirrors_old = conf->mirrors;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 135b1b0a1554..007c2c68dd83 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -1,7 +1,7 @@
#ifndef _RAID10_H
#define _RAID10_H
-struct mirror_info {
+struct raid10_info {
struct md_rdev *rdev, *replacement;
sector_t head_position;
int recovery_disabled; /* matches
@@ -13,8 +13,8 @@ struct mirror_info {
struct r10conf {
struct mddev *mddev;
- struct mirror_info *mirrors;
- struct mirror_info *mirrors_new, *mirrors_old;
+ struct raid10_info *mirrors;
+ struct raid10_info *mirrors_new, *mirrors_old;
spinlock_t device_lock;
/* geometry */
@@ -123,20 +123,6 @@ struct r10bio {
} devs[0];
};
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error. To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio*)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context. So we record
- * the success by setting devs[n].bio to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
/* bits for r10bio.state */
enum r10bio_state {
R10BIO_Uptodate,
@@ -159,4 +145,7 @@ enum r10bio_state {
*/
R10BIO_Previous,
};
+
+extern int md_raid10_congested(struct mddev *mddev, int bits);
+
#endif
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 04348d76bb30..87a2d0bdedd1 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -99,34 +99,40 @@ static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector)
* We maintain a biased count of active stripes in the bottom 16 bits of
* bi_phys_segments, and a count of processed stripes in the upper 16 bits
*/
-static inline int raid5_bi_phys_segments(struct bio *bio)
+static inline int raid5_bi_processed_stripes(struct bio *bio)
{
- return bio->bi_phys_segments & 0xffff;
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ return (atomic_read(segments) >> 16) & 0xffff;
}
-static inline int raid5_bi_hw_segments(struct bio *bio)
+static inline int raid5_dec_bi_active_stripes(struct bio *bio)
{
- return (bio->bi_phys_segments >> 16) & 0xffff;
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ return atomic_sub_return(1, segments) & 0xffff;
}
-static inline int raid5_dec_bi_phys_segments(struct bio *bio)
+static inline void raid5_inc_bi_active_stripes(struct bio *bio)
{
- --bio->bi_phys_segments;
- return raid5_bi_phys_segments(bio);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ atomic_inc(segments);
}
-static inline int raid5_dec_bi_hw_segments(struct bio *bio)
+static inline void raid5_set_bi_processed_stripes(struct bio *bio,
+ unsigned int cnt)
{
- unsigned short val = raid5_bi_hw_segments(bio);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ int old, new;
- --val;
- bio->bi_phys_segments = (val << 16) | raid5_bi_phys_segments(bio);
- return val;
+ do {
+ old = atomic_read(segments);
+ new = (old & 0xffff) | (cnt << 16);
+ } while (atomic_cmpxchg(segments, old, new) != old);
}
-static inline void raid5_set_bi_hw_segments(struct bio *bio, unsigned int cnt)
+static inline void raid5_set_bi_stripes(struct bio *bio, unsigned int cnt)
{
- bio->bi_phys_segments = raid5_bi_phys_segments(bio) | (cnt << 16);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ atomic_set(segments, cnt);
}
/* Find first data disk in a raid6 stripe */
@@ -190,49 +196,56 @@ static int stripe_operations_active(struct stripe_head *sh)
test_bit(STRIPE_COMPUTE_RUN, &sh->state);
}
-static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh)
{
- if (atomic_dec_and_test(&sh->count)) {
- BUG_ON(!list_empty(&sh->lru));
- BUG_ON(atomic_read(&conf->active_stripes)==0);
- if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state) &&
- !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- list_add_tail(&sh->lru, &conf->delayed_list);
- else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- sh->bm_seq - conf->seq_write > 0)
- list_add_tail(&sh->lru, &conf->bitmap_list);
- else {
- clear_bit(STRIPE_DELAYED, &sh->state);
- clear_bit(STRIPE_BIT_DELAY, &sh->state);
- list_add_tail(&sh->lru, &conf->handle_list);
- }
- md_wakeup_thread(conf->mddev->thread);
- } else {
- BUG_ON(stripe_operations_active(sh));
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- if (atomic_dec_return(&conf->preread_active_stripes)
- < IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- atomic_dec(&conf->active_stripes);
- if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
- list_add_tail(&sh->lru, &conf->inactive_list);
- wake_up(&conf->wait_for_stripe);
- if (conf->retry_read_aligned)
- md_wakeup_thread(conf->mddev->thread);
- }
+ BUG_ON(!list_empty(&sh->lru));
+ BUG_ON(atomic_read(&conf->active_stripes)==0);
+ if (test_bit(STRIPE_HANDLE, &sh->state)) {
+ if (test_bit(STRIPE_DELAYED, &sh->state) &&
+ !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ list_add_tail(&sh->lru, &conf->delayed_list);
+ else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+ sh->bm_seq - conf->seq_write > 0)
+ list_add_tail(&sh->lru, &conf->bitmap_list);
+ else {
+ clear_bit(STRIPE_DELAYED, &sh->state);
+ clear_bit(STRIPE_BIT_DELAY, &sh->state);
+ list_add_tail(&sh->lru, &conf->handle_list);
+ }
+ md_wakeup_thread(conf->mddev->thread);
+ } else {
+ BUG_ON(stripe_operations_active(sh));
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ if (atomic_dec_return(&conf->preread_active_stripes)
+ < IO_THRESHOLD)
+ md_wakeup_thread(conf->mddev->thread);
+ atomic_dec(&conf->active_stripes);
+ if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
+ list_add_tail(&sh->lru, &conf->inactive_list);
+ wake_up(&conf->wait_for_stripe);
+ if (conf->retry_read_aligned)
+ md_wakeup_thread(conf->mddev->thread);
}
}
}
+static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+{
+ if (atomic_dec_and_test(&sh->count))
+ do_release_stripe(conf, sh);
+}
+
static void release_stripe(struct stripe_head *sh)
{
struct r5conf *conf = sh->raid_conf;
unsigned long flags;
- spin_lock_irqsave(&conf->device_lock, flags);
- __release_stripe(conf, sh);
- spin_unlock_irqrestore(&conf->device_lock, flags);
+ local_irq_save(flags);
+ if (atomic_dec_and_lock(&sh->count, &conf->device_lock)) {
+ do_release_stripe(conf, sh);
+ spin_unlock(&conf->device_lock);
+ }
+ local_irq_restore(flags);
}
static inline void remove_hash(struct stripe_head *sh)
@@ -640,6 +653,9 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
else
bi->bi_sector = (sh->sector
+ rdev->data_offset);
+ if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+ bi->bi_rw |= REQ_FLUSH;
+
bi->bi_flags = 1 << BIO_UPTODATE;
bi->bi_idx = 0;
bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -749,14 +765,12 @@ static void ops_complete_biofill(void *stripe_head_ref)
{
struct stripe_head *sh = stripe_head_ref;
struct bio *return_bi = NULL;
- struct r5conf *conf = sh->raid_conf;
int i;
pr_debug("%s: stripe %llu\n", __func__,
(unsigned long long)sh->sector);
/* clear completed biofills */
- spin_lock_irq(&conf->device_lock);
for (i = sh->disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
@@ -774,7 +788,7 @@ static void ops_complete_biofill(void *stripe_head_ref)
while (rbi && rbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
rbi2 = r5_next_bio(rbi, dev->sector);
- if (!raid5_dec_bi_phys_segments(rbi)) {
+ if (!raid5_dec_bi_active_stripes(rbi)) {
rbi->bi_next = return_bi;
return_bi = rbi;
}
@@ -782,7 +796,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
}
}
}
- spin_unlock_irq(&conf->device_lock);
clear_bit(STRIPE_BIOFILL_RUN, &sh->state);
return_io(return_bi);
@@ -794,7 +807,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
static void ops_run_biofill(struct stripe_head *sh)
{
struct dma_async_tx_descriptor *tx = NULL;
- struct r5conf *conf = sh->raid_conf;
struct async_submit_ctl submit;
int i;
@@ -805,10 +817,10 @@ static void ops_run_biofill(struct stripe_head *sh)
struct r5dev *dev = &sh->dev[i];
if (test_bit(R5_Wantfill, &dev->flags)) {
struct bio *rbi;
- spin_lock_irq(&conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
dev->read = rbi = dev->toread;
dev->toread = NULL;
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
while (rbi && rbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
tx = async_copy_data(0, rbi, dev->page,
@@ -1144,12 +1156,12 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) {
struct bio *wbi;
- spin_lock_irq(&sh->raid_conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
chosen = dev->towrite;
dev->towrite = NULL;
BUG_ON(dev->written);
wbi = dev->written = chosen;
- spin_unlock_irq(&sh->raid_conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
@@ -1454,6 +1466,8 @@ static int grow_one_stripe(struct r5conf *conf)
init_waitqueue_head(&sh->ops.wait_for_ops);
#endif
+ spin_lock_init(&sh->stripe_lock);
+
if (grow_buffers(sh)) {
shrink_buffers(sh);
kmem_cache_free(conf->slab_cache, sh);
@@ -1739,7 +1753,9 @@ static void raid5_end_read_request(struct bio * bi, int error)
atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
clear_bit(R5_ReadError, &sh->dev[i].flags);
clear_bit(R5_ReWrite, &sh->dev[i].flags);
- }
+ } else if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+ clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+
if (atomic_read(&rdev->read_errors))
atomic_set(&rdev->read_errors, 0);
} else {
@@ -1784,7 +1800,11 @@ static void raid5_end_read_request(struct bio * bi, int error)
else
retry = 1;
if (retry)
- set_bit(R5_ReadError, &sh->dev[i].flags);
+ if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) {
+ set_bit(R5_ReadError, &sh->dev[i].flags);
+ clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+ } else
+ set_bit(R5_ReadNoMerge, &sh->dev[i].flags);
else {
clear_bit(R5_ReadError, &sh->dev[i].flags);
clear_bit(R5_ReWrite, &sh->dev[i].flags);
@@ -2340,11 +2360,18 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
(unsigned long long)bi->bi_sector,
(unsigned long long)sh->sector);
-
- spin_lock_irq(&conf->device_lock);
+ /*
+ * If several bio share a stripe. The bio bi_phys_segments acts as a
+ * reference count to avoid race. The reference count should already be
+ * increased before this function is called (for example, in
+ * make_request()), so other bio sharing this stripe will not free the
+ * stripe. If a stripe is owned by one stripe, the stripe lock will
+ * protect it.
+ */
+ spin_lock_irq(&sh->stripe_lock);
if (forwrite) {
bip = &sh->dev[dd_idx].towrite;
- if (*bip == NULL && sh->dev[dd_idx].written == NULL)
+ if (*bip == NULL)
firstwrite = 1;
} else
bip = &sh->dev[dd_idx].toread;
@@ -2360,7 +2387,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
if (*bip)
bi->bi_next = *bip;
*bip = bi;
- bi->bi_phys_segments++;
+ raid5_inc_bi_active_stripes(bi);
if (forwrite) {
/* check if page is covered */
@@ -2375,7 +2402,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
}
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
(unsigned long long)(*bip)->bi_sector,
@@ -2391,7 +2418,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
overlap:
set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
return 0;
}
@@ -2441,10 +2468,11 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
rdev_dec_pending(rdev, conf->mddev);
}
}
- spin_lock_irq(&conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
/* fail all writes first */
bi = sh->dev[i].towrite;
sh->dev[i].towrite = NULL;
+ spin_unlock_irq(&sh->stripe_lock);
if (bi) {
s->to_write--;
bitmap_end = 1;
@@ -2457,13 +2485,17 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
sh->dev[i].sector + STRIPE_SECTORS) {
struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
md_write_end(conf->mddev);
bi->bi_next = *return_bi;
*return_bi = bi;
}
bi = nextbi;
}
+ if (bitmap_end)
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS, 0, 0);
+ bitmap_end = 0;
/* and fail all 'written' */
bi = sh->dev[i].written;
sh->dev[i].written = NULL;
@@ -2472,7 +2504,7 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
sh->dev[i].sector + STRIPE_SECTORS) {
struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
md_write_end(conf->mddev);
bi->bi_next = *return_bi;
*return_bi = bi;
@@ -2496,14 +2528,13 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
struct bio *nextbi =
r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
bi->bi_next = *return_bi;
*return_bi = bi;
}
bi = nextbi;
}
}
- spin_unlock_irq(&conf->device_lock);
if (bitmap_end)
bitmap_endwrite(conf->mddev->bitmap, sh->sector,
STRIPE_SECTORS, 0, 0);
@@ -2707,30 +2738,23 @@ static void handle_stripe_clean_event(struct r5conf *conf,
test_bit(R5_UPTODATE, &dev->flags)) {
/* We can return any write requests */
struct bio *wbi, *wbi2;
- int bitmap_end = 0;
pr_debug("Return write for disc %d\n", i);
- spin_lock_irq(&conf->device_lock);
wbi = dev->written;
dev->written = NULL;
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
wbi2 = r5_next_bio(wbi, dev->sector);
- if (!raid5_dec_bi_phys_segments(wbi)) {
+ if (!raid5_dec_bi_active_stripes(wbi)) {
md_write_end(conf->mddev);
wbi->bi_next = *return_bi;
*return_bi = wbi;
}
wbi = wbi2;
}
- if (dev->towrite == NULL)
- bitmap_end = 1;
- spin_unlock_irq(&conf->device_lock);
- if (bitmap_end)
- bitmap_endwrite(conf->mddev->bitmap,
- sh->sector,
- STRIPE_SECTORS,
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS,
!test_bit(STRIPE_DEGRADED, &sh->state),
- 0);
+ 0);
}
}
@@ -3182,7 +3206,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
/* Now to look around and see what can be done */
rcu_read_lock();
- spin_lock_irq(&conf->device_lock);
for (i=disks; i--; ) {
struct md_rdev *rdev;
sector_t first_bad;
@@ -3328,7 +3351,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
do_recovery = 1;
}
}
- spin_unlock_irq(&conf->device_lock);
if (test_bit(STRIPE_SYNCING, &sh->state)) {
/* If there is a failed device being replaced,
* we must be recovering.
@@ -3791,7 +3813,7 @@ static struct bio *remove_bio_from_retry(struct r5conf *conf)
* this sets the active strip count to 1 and the processed
* strip count to zero (upper 8 bits)
*/
- bi->bi_phys_segments = 1; /* biased count of active stripes */
+ raid5_set_bi_stripes(bi, 1); /* biased count of active stripes */
}
return bi;
@@ -4113,7 +4135,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
finish_wait(&conf->wait_for_overlap, &w);
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
- if ((bi->bi_rw & REQ_SYNC) &&
+ if ((bi->bi_rw & REQ_NOIDLE) &&
!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
atomic_inc(&conf->preread_active_stripes);
mddev_check_plugged(mddev);
@@ -4126,9 +4148,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
}
}
- spin_lock_irq(&conf->device_lock);
- remaining = raid5_dec_bi_phys_segments(bi);
- spin_unlock_irq(&conf->device_lock);
+ remaining = raid5_dec_bi_active_stripes(bi);
if (remaining == 0) {
if ( rw == WRITE )
@@ -4484,7 +4504,7 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
sector += STRIPE_SECTORS,
scnt++) {
- if (scnt < raid5_bi_hw_segments(raid_bio))
+ if (scnt < raid5_bi_processed_stripes(raid_bio))
/* already done this stripe */
continue;
@@ -4492,25 +4512,24 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
if (!sh) {
/* failed to get a stripe - must wait */
- raid5_set_bi_hw_segments(raid_bio, scnt);
+ raid5_set_bi_processed_stripes(raid_bio, scnt);
conf->retry_read_aligned = raid_bio;
return handled;
}
if (!add_stripe_bio(sh, raid_bio, dd_idx, 0)) {
release_stripe(sh);
- raid5_set_bi_hw_segments(raid_bio, scnt);
+ raid5_set_bi_processed_stripes(raid_bio, scnt);
conf->retry_read_aligned = raid_bio;
return handled;
}
+ set_bit(R5_ReadNoMerge, &sh->dev[dd_idx].flags);
handle_stripe(sh);
release_stripe(sh);
handled++;
}
- spin_lock_irq(&conf->device_lock);
- remaining = raid5_dec_bi_phys_segments(raid_bio);
- spin_unlock_irq(&conf->device_lock);
+ remaining = raid5_dec_bi_active_stripes(raid_bio);
if (remaining == 0)
bio_endio(raid_bio, 0);
if (atomic_dec_and_test(&conf->active_aligned_reads))
@@ -4543,7 +4562,7 @@ static void raid5d(struct mddev *mddev)
while (1) {
struct bio *bio;
- if (atomic_read(&mddev->plug_cnt) == 0 &&
+ if (
!list_empty(&conf->bitmap_list)) {
/* Now is a good time to flush some bitmap updates */
conf->seq_flush++;
@@ -4553,8 +4572,7 @@ static void raid5d(struct mddev *mddev)
conf->seq_write = conf->seq_flush;
activate_bit_delay(conf);
}
- if (atomic_read(&mddev->plug_cnt) == 0)
- raid5_activate_delayed(conf);
+ raid5_activate_delayed(conf);
while ((bio = remove_bio_from_retry(conf))) {
int ok;
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 2164021f3b5f..61dbb615c30b 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -210,6 +210,7 @@ struct stripe_head {
int disks; /* disks in stripe */
enum check_states check_state;
enum reconstruct_states reconstruct_state;
+ spinlock_t stripe_lock;
/**
* struct stripe_operations
* @target - STRIPE_OP_COMPUTE_BLK target
@@ -273,6 +274,7 @@ enum r5dev_flags {
R5_Wantwrite,
R5_Overlap, /* There is a pending overlapping request
* on this block */
+ R5_ReadNoMerge, /* prevent bio from merging in block-layer */
R5_ReadError, /* seen a read error here recently */
R5_ReWrite, /* have tried to over-write the readerror */
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 9575db429df4..d941581ab921 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -6,20 +6,82 @@ menuconfig MEDIA_SUPPORT
tristate "Multimedia support"
depends on HAS_IOMEM
help
- If you want to use Video for Linux, DVB for Linux, or DAB adapters,
+ If you want to use Webcams, Video grabber devices and/or TV devices
enable this option and other options below.
+ Additional info and docs are available on the web at
+ <http://linuxtv.org>
if MEDIA_SUPPORT
comment "Multimedia core support"
#
+# Multimedia support - automatically enable V4L2 and DVB core
+#
+config MEDIA_CAMERA_SUPPORT
+ bool "Cameras/video grabbers support"
+ ---help---
+ Enable support for webcams and video grabbers.
+
+ Say Y when you have a webcam or a video capture grabber board.
+
+config MEDIA_ANALOG_TV_SUPPORT
+ bool "Analog TV support"
+ ---help---
+ Enable analog TV support.
+
+ Say Y when you have a TV board with analog support or with a
+ hybrid analog/digital TV chipset.
+
+ Note: There are several DVB cards that are based on chips that
+ support both analog and digital TV. Disabling this option
+ will disable support for them.
+
+config MEDIA_DIGITAL_TV_SUPPORT
+ bool "Digital TV support"
+ ---help---
+ Enable digital TV support.
+
+ Say Y when you have a board with digital support or a board with
+ hybrid digital TV and analog TV.
+
+config MEDIA_RADIO_SUPPORT
+ bool "AM/FM radio receivers/transmitters support"
+ ---help---
+ Enable AM/FM radio support.
+
+ Additional info and docs are available on the web at
+ <http://linuxtv.org>
+
+ Say Y when you have a board with radio support.
+
+ Note: There are several TV cards that are based on chips that
+ support radio reception. Disabling this option will
+ disable support for them.
+
+config MEDIA_RC_SUPPORT
+ bool "Remote Controller support"
+ depends on INPUT
+ ---help---
+ Enable support for Remote Controllers on Linux. This is
+ needed in order to support several video capture adapters,
+ standalone IR receivers/transmitters, and RF receivers.
+
+ Enable this option if you have a video capture board even
+ if you don't need IR, as otherwise, you may not be able to
+ compile the driver for your adapter.
+
+ Say Y when you have a TV or an IR device.
+
+#
# Media controller
+# Selectable only for webcam/grabbers, as other drivers don't use it
#
config MEDIA_CONTROLLER
bool "Media Controller API (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ depends on MEDIA_CAMERA_SUPPORT
---help---
Enable the media controller API used to query media devices internal
topology and configure it dynamically.
@@ -27,26 +89,15 @@ config MEDIA_CONTROLLER
This API is mostly used by camera interfaces in embedded platforms.
#
-# V4L core and enabled API's
+# Video4Linux support
+# Only enables if one of the V4L2 types (ATV, webcam, radio) is selected
#
config VIDEO_DEV
- tristate "Video For Linux"
- ---help---
- V4L core support for video capture and overlay devices, webcams and
- AM/FM radio cards.
-
- This kernel includes support for the new Video for Linux Two API,
- (V4L2).
-
- Additional info and docs are available on the web at
- <http://linuxtv.org>
-
- Documentation for V4L2 is also available on the web at
- <http://bytesex.org/v4l/>.
-
- To compile this driver as a module, choose M here: the
- module will be called videodev.
+ tristate
+ depends on MEDIA_SUPPORT
+ depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT
+ default y
config VIDEO_V4L2_COMMON
tristate
@@ -64,25 +115,15 @@ config VIDEO_V4L2_SUBDEV_API
#
# DVB Core
+# Only enables if one of DTV is selected
#
config DVB_CORE
- tristate "DVB for Linux"
+ tristate
+ depends on MEDIA_SUPPORT
+ depends on MEDIA_DIGITAL_TV_SUPPORT
+ default y
select CRC32
- help
- DVB core utility functions for device handling, software fallbacks etc.
-
- Enable this if you own a DVB/ATSC adapter and want to use it or if
- you compile Linux for a digital SetTopBox.
-
- Say Y when you have a DVB or an ATSC card and want to use it.
-
- API specs and user tools are available from <http://www.linuxtv.org/>.
-
- Please report problems regarding this support to the LinuxDVB
- mailing list.
-
- If unsure say N.
config DVB_NET
bool "DVB Network Support"
@@ -97,12 +138,7 @@ config DVB_NET
You may want to disable the network support on embedded devices. If
unsure say Y.
-config VIDEO_MEDIA
- tristate
- default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV)
-
-comment "Multimedia drivers"
-
+comment "Media drivers"
source "drivers/media/common/Kconfig"
source "drivers/media/rc/Kconfig"
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index bbf4945149a9..94c6ff7a5da3 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -1,7 +1,8 @@
config MEDIA_ATTACH
bool "Load and attach frontend and tuner driver modules as needed"
- depends on VIDEO_MEDIA
+ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
depends on MODULES
+ default y if !EXPERT
help
Remove the static dependency of DVB card drivers on all
frontend modules for all possible card variants. Instead,
@@ -19,15 +20,15 @@ config MEDIA_ATTACH
config MEDIA_TUNER
tristate
- default VIDEO_MEDIA && I2C
- depends on VIDEO_MEDIA && I2C
+ depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C
+ default y
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && EXPERIMENTAL
- select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT && EXPERIMENTAL
+ select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
@@ -47,10 +48,11 @@ config MEDIA_TUNER_CUSTOMISE
menu "Customize TV tuners"
visible if MEDIA_TUNER_CUSTOMISE
+ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
config MEDIA_TUNER_SIMPLE
tristate "Simple tuner support"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
select MEDIA_TUNER_TDA9887
default m if MEDIA_TUNER_CUSTOMISE
help
@@ -58,7 +60,7 @@ config MEDIA_TUNER_SIMPLE
config MEDIA_TUNER_TDA8290
tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
select MEDIA_TUNER_TDA827X
select MEDIA_TUNER_TDA18271
default m if MEDIA_TUNER_CUSTOMISE
@@ -67,21 +69,21 @@ config MEDIA_TUNER_TDA8290
config MEDIA_TUNER_TDA827X
tristate "Philips TDA827X silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
config MEDIA_TUNER_TDA18271
tristate "NXP TDA18271 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A silicon tuner module. Say Y when you want to support this tuner.
config MEDIA_TUNER_TDA9887
tristate "TDA 9885/6/7 analog IF demodulator"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Say Y here to include support for Philips TDA9885/6/7
@@ -89,7 +91,7 @@ config MEDIA_TUNER_TDA9887
config MEDIA_TUNER_TEA5761
tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
depends on EXPERIMENTAL
default m if MEDIA_TUNER_CUSTOMISE
help
@@ -97,63 +99,63 @@ config MEDIA_TUNER_TEA5761
config MEDIA_TUNER_TEA5767
tristate "TEA 5767 radio tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Say Y here to include support for the Philips TEA5767 radio tuner.
config MEDIA_TUNER_MT20XX
tristate "Microtune 2032 / 2050 tuners"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Say Y here to include support for the MT2032 / MT2050 tuner.
config MEDIA_TUNER_MT2060
tristate "Microtune MT2060 silicon IF tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon IF tuner MT2060 from Microtune.
config MEDIA_TUNER_MT2063
tristate "Microtune MT2063 silicon IF tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon IF tuner MT2063 from Microtune.
config MEDIA_TUNER_MT2266
tristate "Microtune MT2266 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon baseband tuner MT2266 from Microtune.
config MEDIA_TUNER_MT2131
tristate "Microtune MT2131 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon baseband tuner MT2131 from Microtune.
config MEDIA_TUNER_QT1010
tristate "Quantek QT1010 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner QT1010 from Quantek.
config MEDIA_TUNER_XC2028
tristate "XCeive xc2028/xc3028 tuners"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Say Y here to include support for the xc2028/xc3028 tuners.
config MEDIA_TUNER_XC5000
tristate "Xceive XC5000 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner XC5000 from Xceive.
@@ -162,7 +164,7 @@ config MEDIA_TUNER_XC5000
config MEDIA_TUNER_XC4000
tristate "Xceive XC4000 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner XC4000 from Xceive.
@@ -171,70 +173,70 @@ config MEDIA_TUNER_XC4000
config MEDIA_TUNER_MXL5005S
tristate "MaxLinear MSL5005S silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner MXL5005S from MaxLinear.
config MEDIA_TUNER_MXL5007T
tristate "MaxLinear MxL5007T silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner MxL5007T from MaxLinear.
config MEDIA_TUNER_MC44S803
tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Freescale MC44S803 based tuners
config MEDIA_TUNER_MAX2165
tristate "Maxim MAX2165 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
A driver for the silicon tuner MAX2165 from Maxim.
config MEDIA_TUNER_TDA18218
tristate "NXP TDA18218 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
NXP TDA18218 silicon tuner driver.
config MEDIA_TUNER_FC0011
tristate "Fitipower FC0011 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Fitipower FC0011 silicon tuner driver.
config MEDIA_TUNER_FC0012
tristate "Fitipower FC0012 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Fitipower FC0012 silicon tuner driver.
config MEDIA_TUNER_FC0013
tristate "Fitipower FC0013 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Fitipower FC0013 silicon tuner driver.
config MEDIA_TUNER_TDA18212
tristate "NXP TDA18212 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
NXP TDA18212 silicon tuner driver.
config MEDIA_TUNER_TUA9001
tristate "Infineon TUA 9001 silicon tuner"
- depends on VIDEO_MEDIA && I2C
+ depends on MEDIA_SUPPORT && I2C
default m if MEDIA_TUNER_CUSTOMISE
help
Infineon TUA 9001 silicon tuner driver.
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index b5ee3ebfcfca..ea0550eafe7d 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -90,11 +90,22 @@ struct firmware_properties {
int scode_nr;
};
+enum xc2028_state {
+ XC2028_NO_FIRMWARE = 0,
+ XC2028_WAITING_FIRMWARE,
+ XC2028_ACTIVE,
+ XC2028_SLEEP,
+ XC2028_NODEV,
+};
+
struct xc2028_data {
struct list_head hybrid_tuner_instance_list;
struct tuner_i2c_props i2c_props;
__u32 frequency;
+ enum xc2028_state state;
+ const char *fname;
+
struct firmware_description *firm;
int firm_size;
__u16 firm_version;
@@ -255,6 +266,21 @@ static v4l2_std_id parse_audio_std_option(void)
return 0;
}
+static int check_device_status(struct xc2028_data *priv)
+{
+ switch (priv->state) {
+ case XC2028_NO_FIRMWARE:
+ case XC2028_WAITING_FIRMWARE:
+ return -EAGAIN;
+ case XC2028_ACTIVE:
+ case XC2028_SLEEP:
+ return 0;
+ case XC2028_NODEV:
+ return -ENODEV;
+ }
+ return 0;
+}
+
static void free_firmware(struct xc2028_data *priv)
{
int i;
@@ -270,45 +296,28 @@ static void free_firmware(struct xc2028_data *priv)
priv->firm = NULL;
priv->firm_size = 0;
+ priv->state = XC2028_NO_FIRMWARE;
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
}
-static int load_all_firmwares(struct dvb_frontend *fe)
+static int load_all_firmwares(struct dvb_frontend *fe,
+ const struct firmware *fw)
{
struct xc2028_data *priv = fe->tuner_priv;
- const struct firmware *fw = NULL;
const unsigned char *p, *endp;
int rc = 0;
int n, n_array;
char name[33];
- char *fname;
tuner_dbg("%s called\n", __func__);
- if (!firmware_name[0])
- fname = priv->ctrl.fname;
- else
- fname = firmware_name;
-
- tuner_dbg("Reading firmware %s\n", fname);
- rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
- if (rc < 0) {
- if (rc == -ENOENT)
- tuner_err("Error: firmware %s not found.\n",
- fname);
- else
- tuner_err("Error %d while requesting firmware %s \n",
- rc, fname);
-
- return rc;
- }
p = fw->data;
endp = p + fw->size;
if (fw->size < sizeof(name) - 1 + 2 + 2) {
tuner_err("Error: firmware file %s has invalid size!\n",
- fname);
+ priv->fname);
goto corrupt;
}
@@ -323,7 +332,7 @@ static int load_all_firmwares(struct dvb_frontend *fe)
p += 2;
tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
- n_array, fname, name,
+ n_array, priv->fname, name,
priv->firm_version >> 8, priv->firm_version & 0xff);
priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
@@ -417,9 +426,10 @@ err:
free_firmware(priv);
done:
- release_firmware(fw);
if (rc == 0)
tuner_dbg("Firmware files loaded.\n");
+ else
+ priv->state = XC2028_NODEV;
return rc;
}
@@ -707,22 +717,15 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type,
{
struct xc2028_data *priv = fe->tuner_priv;
struct firmware_properties new_fw;
- int rc = 0, retry_count = 0;
+ int rc, retry_count = 0;
u16 version, hwmodel;
v4l2_std_id std0;
tuner_dbg("%s called\n", __func__);
- if (!priv->firm) {
- if (!priv->ctrl.fname) {
- tuner_info("xc2028/3028 firmware name not set!\n");
- return -EINVAL;
- }
-
- rc = load_all_firmwares(fe);
- if (rc < 0)
- return rc;
- }
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
if (priv->ctrl.mts && !(type & FM))
type |= MTS;
@@ -749,9 +752,13 @@ retry:
printk("scode_nr %d\n", new_fw.scode_nr);
}
- /* No need to reload base firmware if it matches */
- if (((BASE | new_fw.type) & BASE_TYPES) ==
- (priv->cur_fw.type & BASE_TYPES)) {
+ /*
+ * No need to reload base firmware if it matches and if the tuner
+ * is not at sleep mode
+ */
+ if ((priv->state == XC2028_ACTIVE) &&
+ (((BASE | new_fw.type) & BASE_TYPES) ==
+ (priv->cur_fw.type & BASE_TYPES))) {
tuner_dbg("BASE firmware not changed.\n");
goto skip_base;
}
@@ -872,10 +879,13 @@ read_not_reliable:
* 2. Tell whether BASE firmware was just changed the next time through.
*/
priv->cur_fw.type |= BASE;
+ priv->state = XC2028_ACTIVE;
return 0;
fail:
+ priv->state = XC2028_SLEEP;
+
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
if (retry_count < 8) {
msleep(50);
@@ -893,28 +903,39 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
{
struct xc2028_data *priv = fe->tuner_priv;
u16 frq_lock, signal = 0;
- int rc;
+ int rc, i;
tuner_dbg("%s called\n", __func__);
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
mutex_lock(&priv->lock);
/* Sync Lock Indicator */
- rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
- if (rc < 0)
- goto ret;
+ for (i = 0; i < 3; i++) {
+ rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+ if (rc < 0)
+ goto ret;
- /* Frequency is locked */
- if (frq_lock == 1)
- signal = 1 << 11;
+ if (frq_lock)
+ break;
+ msleep(6);
+ }
+
+ /* Frequency didn't lock */
+ if (frq_lock == 2)
+ goto ret;
/* Get SNR of the video signal */
rc = xc2028_get_reg(priv, XREG_SNR, &signal);
if (rc < 0)
goto ret;
- /* Use both frq_lock and signal to generate the result */
- signal = signal || ((signal & 0x07) << 12);
+ /* Signal level is 3 bits only */
+
+ signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
ret:
mutex_unlock(&priv->lock);
@@ -926,6 +947,49 @@ ret:
return rc;
}
+static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int i, rc;
+ u16 frq_lock = 0;
+ s16 afc_reg = 0;
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&priv->lock);
+
+ /* Sync Lock Indicator */
+ for (i = 0; i < 3; i++) {
+ rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+ if (rc < 0)
+ goto ret;
+
+ if (frq_lock)
+ break;
+ msleep(6);
+ }
+
+ /* Frequency didn't lock */
+ if (frq_lock == 2)
+ goto ret;
+
+ /* Get AFC */
+ rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
+ if (rc < 0)
+ goto ret;
+
+ *afc = afc_reg * 15625; /* Hz */
+
+ tuner_dbg("AFC is %d Hz\n", *afc);
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
#define DIV 15625
static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
@@ -1111,11 +1175,16 @@ static int xc2028_set_params(struct dvb_frontend *fe)
u32 delsys = c->delivery_system;
u32 bw = c->bandwidth_hz;
struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
+ int rc;
+ unsigned int type = 0;
u16 demod = 0;
tuner_dbg("%s called\n", __func__);
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
switch (delsys) {
case SYS_DVBT:
case SYS_DVBT2:
@@ -1201,7 +1270,11 @@ static int xc2028_set_params(struct dvb_frontend *fe)
static int xc2028_sleep(struct dvb_frontend *fe)
{
struct xc2028_data *priv = fe->tuner_priv;
- int rc = 0;
+ int rc;
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
/* Avoid firmware reload on slow devices or if PM disabled */
if (no_poweroff || priv->ctrl.disable_power_mgmt)
@@ -1220,7 +1293,7 @@ static int xc2028_sleep(struct dvb_frontend *fe)
else
rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
- priv->cur_fw.type = 0; /* need firmware reload */
+ priv->state = XC2028_SLEEP;
mutex_unlock(&priv->lock);
@@ -1237,8 +1310,9 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
/* only perform final cleanup if this is the last instance */
if (hybrid_tuner_report_instance_count(priv) == 1) {
- kfree(priv->ctrl.fname);
free_firmware(priv);
+ kfree(priv->ctrl.fname);
+ priv->ctrl.fname = NULL;
}
if (priv)
@@ -1254,14 +1328,42 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
tuner_dbg("%s called\n", __func__);
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
*frequency = priv->frequency;
return 0;
}
+static void load_firmware_cb(const struct firmware *fw,
+ void *context)
+{
+ struct dvb_frontend *fe = context;
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
+
+ tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
+ if (!fw) {
+ tuner_err("Could not load firmware %s.\n", priv->fname);
+ priv->state = XC2028_NODEV;
+ return;
+ }
+
+ rc = load_all_firmwares(fe, fw);
+
+ release_firmware(fw);
+
+ if (rc < 0)
+ return;
+ priv->state = XC2028_SLEEP;
+}
+
static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
struct xc2028_data *priv = fe->tuner_priv;
@@ -1272,21 +1374,49 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
mutex_lock(&priv->lock);
+ /*
+ * Copy the config data.
+ * For the firmware name, keep a local copy of the string,
+ * in order to avoid troubles during device release.
+ */
+ if (priv->ctrl.fname)
+ kfree(priv->ctrl.fname);
memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
- if (priv->ctrl.max_len < 9)
- priv->ctrl.max_len = 13;
-
if (p->fname) {
- if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) {
- kfree(priv->ctrl.fname);
- free_firmware(priv);
- }
-
priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
if (priv->ctrl.fname == NULL)
rc = -ENOMEM;
}
+ /*
+ * If firmware name changed, frees firmware. As free_firmware will
+ * reset the status to NO_FIRMWARE, this forces a new request_firmware
+ */
+ if (!firmware_name[0] && p->fname &&
+ priv->fname && strcmp(p->fname, priv->fname))
+ free_firmware(priv);
+
+ if (priv->ctrl.max_len < 9)
+ priv->ctrl.max_len = 13;
+
+ if (priv->state == XC2028_NO_FIRMWARE) {
+ if (!firmware_name[0])
+ priv->fname = priv->ctrl.fname;
+ else
+ priv->fname = firmware_name;
+
+ rc = request_firmware_nowait(THIS_MODULE, 1,
+ priv->fname,
+ priv->i2c_props.adap->dev.parent,
+ GFP_KERNEL,
+ fe, load_firmware_cb);
+ if (rc < 0) {
+ tuner_err("Failed to request firmware %s\n",
+ priv->fname);
+ priv->state = XC2028_NODEV;
+ }
+ priv->state = XC2028_WAITING_FIRMWARE;
+ }
mutex_unlock(&priv->lock);
return rc;
@@ -1305,6 +1435,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
.release = xc2028_dvb_release,
.get_frequency = xc2028_get_frequency,
.get_rf_strength = xc2028_signal,
+ .get_afc = xc2028_get_afc,
.set_params = xc2028_set_params,
.sleep = xc2028_sleep,
};
@@ -1375,3 +1506,5 @@ MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
+MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index dcca42ca57be..362a8d7c9738 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -210,13 +210,15 @@ struct xc5000_fw_cfg {
u16 size;
};
+#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
- .name = "dvb-fe-xc5000-1.6.114.fw",
+ .name = XC5000A_FIRMWARE,
.size = 12401,
};
+#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
- .name = "dvb-fe-xc5000c-41.024.5.fw",
+ .name = XC5000C_FIRMWARE,
.size = 16497,
};
@@ -717,6 +719,12 @@ static int xc5000_set_params(struct dvb_frontend *fe)
priv->freq_hz = freq - 1750000;
priv->video_standard = DTV6;
break;
+ case SYS_ISDBT:
+ /* All ISDB-T are currently for 6 MHz bw */
+ if (!bw)
+ bw = 6000000;
+ /* fall to OFDM handling */
+ case SYS_DMBTH:
case SYS_DVBT:
case SYS_DVBT2:
dprintk(1, "%s() OFDM\n", __func__);
@@ -1253,3 +1261,5 @@ EXPORT_SYMBOL(xc5000_attach);
MODULE_AUTHOR("Steven Toth");
MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC5000A_FIRMWARE);
+MODULE_FIRMWARE(XC5000C_FIRMWARE);
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
index 131b938e9e81..ebf3f05839d2 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input)
memset(&config, 0, sizeof(config));
config.microcode_name = "drxk_a3.mc";
+ config.qam_demod_parameter_count = 4;
config.adr = 0x29 + (input->nr & 1);
fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index e929d5697b87..7c64c09103a9 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -220,6 +220,7 @@ struct dvb_tuner_ops {
#define TUNER_STATUS_STEREO 2
int (*get_status)(struct dvb_frontend *fe, u32 *status);
int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
+ int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
/** These are provided separately from set_params in order to facilitate silicon
* tuners which require sophisticated tuning loops, controlling each parameter separately. */
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index a26949336b3d..c2161565023a 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -418,9 +418,12 @@ config DVB_USB_RTL28XXU
tristate "Realtek RTL28xxU DVB USB support"
depends on DVB_USB && EXPERIMENTAL
select DVB_RTL2830
+ select DVB_RTL2832
select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Realtek RTL28xxU DVB USB receiver.
diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c
index 4008b9c50fbd..86861e6f86d2 100644
--- a/drivers/media/dvb/dvb-usb/az6007.c
+++ b/drivers/media/dvb/dvb-usb/az6007.c
@@ -590,12 +590,10 @@ static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
int ret;
ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
- memcpy(mac, st->data, sizeof(mac));
+ memcpy(mac, st->data, 6);
if (ret > 0)
- deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n",
- __func__, mac[0], mac[1], mac[2],
- mac[3], mac[4], mac[5]);
+ deb_info("%s: mac is %pM\n", __func__, mac);
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 7a6160bf54ba..26c44818a5ab 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -100,6 +100,7 @@
#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397
#define USB_PID_CONEXANT_D680_DMB 0x86d6
#define USB_PID_CREATIX_CTX1921 0x1921
+#define USB_PID_DELOCK_USB2_DVBT 0xb803
#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
@@ -160,6 +161,7 @@
#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097
#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099
+#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
#define USB_PID_TWINHAN_VP7041_WARM 0x3202
#define USB_PID_TWINHAN_VP7020_COLD 0x3203
@@ -245,6 +247,7 @@
#define USB_PID_TERRATEC_H7_2 0x10a3
#define USB_PID_TERRATEC_T3 0x10a0
#define USB_PID_TERRATEC_T5 0x10a1
+#define USB_PID_NOXON_DAB_STICK 0x00b3
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
#define USB_PID_PINNACLE_PCTV2000E 0x022c
#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
index 41e1f5537f44..6bd0bd792437 100644
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.c
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
* Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@googlemail.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
@@ -22,10 +23,13 @@
#include "rtl28xxu.h"
#include "rtl2830.h"
+#include "rtl2832.h"
#include "qt1010.h"
#include "mt2060.h"
#include "mxl5005s.h"
+#include "fc0012.h"
+#include "fc0013.h"
/* debug */
static int dvb_usb_rtl28xxu_debug;
@@ -80,7 +84,7 @@ err:
return ret;
}
-static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
{
struct rtl28xxu_req req;
@@ -116,12 +120,12 @@ static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
return rtl28xxu_ctrl_msg(d, &req);
}
-static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
{
- return rtl2831_wr_regs(d, reg, &val, 1);
+ return rtl28xx_wr_regs(d, reg, &val, 1);
}
-static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
{
return rtl2831_rd_regs(d, reg, val, 1);
}
@@ -308,12 +312,12 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
*/
/* GPIO direction */
- ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
if (ret)
goto err;
/* enable as output GPIO0, GPIO2, GPIO4 */
- ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
if (ret)
goto err;
@@ -381,34 +385,159 @@ err:
return ret;
}
+static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .if_dvbt = 0,
+ .tuner = TUNER_RTL2832_FC0012
+};
+
+static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .if_dvbt = 0,
+ .tuner = TUNER_RTL2832_FC0013
+};
+
+static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+ u8 val;
+
+ deb_info("%s cmd=%d arg=%d\n", __func__, cmd, arg);
+ switch (cmd) {
+ case FC_FE_CALLBACK_VHF_ENABLE:
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ if (arg)
+ val &= 0xbf; /* set GPIO6 low */
+ else
+ val |= 0x40; /* set GPIO6 high */
+
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ return 0;
+
+err:
+ err("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+
+static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ /* TODO implement*/
+ return 0;
+}
+
+static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+ struct rtl28xxu_priv *priv = d->priv;
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC0012:
+ return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
+
+ case TUNER_RTL2832_FC0013:
+ return rtl2832u_fc0013_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return -ENODEV;
+}
+
+static int rtl2832u_frontend_callback(void *adapter_priv, int component,
+ int cmd, int arg)
+{
+ struct i2c_adapter *adap = adapter_priv;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+ switch (component) {
+ case DVB_FRONTEND_COMPONENT_TUNER:
+ return rtl2832u_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+
+
+
static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
struct rtl28xxu_priv *priv = adap->dev->priv;
- u8 buf[1];
+ struct rtl2832_config *rtl2832_config;
+
+ u8 buf[2], val;
/* open RTL2832U/RTL2832 I2C gate */
struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
/* close RTL2832U/RTL2832 I2C gate */
struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+ /* for FC0012 tuner probe */
+ struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf};
+ /* for FC0013 tuner probe */
+ struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf};
+ /* for MT2266 tuner probe */
+ struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf};
/* for FC2580 tuner probe */
struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+ /* for MT2063 tuner probe */
+ struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf};
+ /* for MAX3543 tuner probe */
+ struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf};
+ /* for TUA9001 tuner probe */
+ struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf};
+ /* for MXL5007T tuner probe */
+ struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf};
+ /* for E4000 tuner probe */
+ struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf};
+ /* for TDA18272 tuner probe */
+ struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
deb_info("%s:\n", __func__);
- /* GPIO direction */
- ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+
+ ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_DIR, &val);
if (ret)
goto err;
- /* enable as output GPIO0, GPIO2, GPIO4 */
- ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ val &= 0xbf;
+
+ ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, val);
if (ret)
goto err;
- ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
+
+ /* enable as output GPIO3 and GPIO6*/
+ ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_EN, &val);
if (ret)
goto err;
+ val |= 0x48;
+
+ ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, val);
+ if (ret)
+ goto err;
+
+
+
/*
* Probe used tuner. We need to know used tuner before demod attach
* since there is some demod params needed to set according to tuner.
@@ -419,25 +548,108 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
if (ret)
goto err;
+ priv->tuner = TUNER_NONE;
+
+ /* check FC0012 ID register; reg=00 val=a1 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0012);
+ if (ret == 0 && buf[0] == 0xa1) {
+ priv->tuner = TUNER_RTL2832_FC0012;
+ rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+ info("%s: FC0012 tuner found", __func__);
+ goto found;
+ }
+
+ /* check FC0013 ID register; reg=00 val=a3 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0013);
+ if (ret == 0 && buf[0] == 0xa3) {
+ priv->tuner = TUNER_RTL2832_FC0013;
+ rtl2832_config = &rtl28xxu_rtl2832_fc0013_config;
+ info("%s: FC0013 tuner found", __func__);
+ goto found;
+ }
+
+ /* check MT2266 ID register; reg=00 val=85 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2266);
+ if (ret == 0 && buf[0] == 0x85) {
+ priv->tuner = TUNER_RTL2832_MT2266;
+ /* TODO implement tuner */
+ info("%s: MT2266 tuner found", __func__);
+ goto unsupported;
+ }
+
/* check FC2580 ID register; reg=01 val=56 */
ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
if (ret == 0 && buf[0] == 0x56) {
priv->tuner = TUNER_RTL2832_FC2580;
- deb_info("%s: FC2580\n", __func__);
- goto found;
- } else {
- deb_info("%s: FC2580 probe failed=%d - %02x\n",
- __func__, ret, buf[0]);
+ /* TODO implement tuner */
+ info("%s: FC2580 tuner found", __func__);
+ goto unsupported;
}
+ /* check MT2063 ID register; reg=00 val=9e || 9c */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2063);
+ if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
+ priv->tuner = TUNER_RTL2832_MT2063;
+ /* TODO implement tuner */
+ info("%s: MT2063 tuner found", __func__);
+ goto unsupported;
+ }
+
+ /* check MAX3543 ID register; reg=00 val=38 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_max3543);
+ if (ret == 0 && buf[0] == 0x38) {
+ priv->tuner = TUNER_RTL2832_MAX3543;
+ /* TODO implement tuner */
+ info("%s: MAX3534 tuner found", __func__);
+ goto unsupported;
+ }
+
+ /* check TUA9001 ID register; reg=7e val=2328 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_tua9001);
+ if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
+ priv->tuner = TUNER_RTL2832_TUA9001;
+ /* TODO implement tuner */
+ info("%s: TUA9001 tuner found", __func__);
+ goto unsupported;
+ }
+
+ /* check MXL5007R ID register; reg=d9 val=14 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mxl5007t);
+ if (ret == 0 && buf[0] == 0x14) {
+ priv->tuner = TUNER_RTL2832_MXL5007T;
+ /* TODO implement tuner */
+ info("%s: MXL5007T tuner found", __func__);
+ goto unsupported;
+ }
+
+ /* check E4000 ID register; reg=02 val=40 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_e4000);
+ if (ret == 0 && buf[0] == 0x40) {
+ priv->tuner = TUNER_RTL2832_E4000;
+ /* TODO implement tuner */
+ info("%s: E4000 tuner found", __func__);
+ goto unsupported;
+ }
+
+ /* check TDA18272 ID register; reg=00 val=c760 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_tda18272);
+ if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
+ priv->tuner = TUNER_RTL2832_TDA18272;
+ /* TODO implement tuner */
+ info("%s: TDA18272 tuner found", __func__);
+ goto unsupported;
+ }
+
+unsupported:
/* close demod I2C gate */
ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
if (ret)
goto err;
/* tuner not found */
+ deb_info("No compatible tuner found");
ret = -ENODEV;
- goto err;
+ return ret;
found:
/* close demod I2C gate */
@@ -446,9 +658,18 @@ found:
goto err;
/* attach demodulator */
- /* TODO: */
+ adap->fe_adap[0].fe = dvb_attach(rtl2832_attach, rtl2832_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* set fe callbacks */
+ adap->fe_adap[0].fe->callback = rtl2832u_frontend_callback;
return ret;
+
err:
deb_info("%s: failed=%d\n", __func__, ret);
return ret;
@@ -531,10 +752,24 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
deb_info("%s:\n", __func__);
switch (priv->tuner) {
- case TUNER_RTL2832_FC2580:
- /* TODO: */
- fe = NULL;
+ case TUNER_RTL2832_FC0012:
+ fe = dvb_attach(fc0012_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+ /* since fc0012 includs reading the signal strength delegate
+ * that to the tuner driver */
+ adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0].
+ fe->ops.tuner_ops.get_rf_strength;
+ return 0;
break;
+ case TUNER_RTL2832_FC0013:
+ fe = dvb_attach(fc0013_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+ /* fc0013 also supports signal strength reading */
+ adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0]
+ .fe->ops.tuner_ops.get_rf_strength;
+ return 0;
default:
fe = NULL;
err("unknown tuner=%d", priv->tuner);
@@ -551,14 +786,14 @@ err:
return ret;
}
-static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+static int rtl2831u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
{
int ret;
u8 buf[2], gpio;
deb_info("%s: onoff=%d\n", __func__, onoff);
- ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
+ ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
if (ret)
goto err;
@@ -572,11 +807,37 @@ static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
gpio &= (~0x04); /* LED off */
}
- ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
+ ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
if (ret)
goto err;
- ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+ ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+{
+ int ret;
+ u8 buf[2];
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+
+ if (onoff) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ } else {
+ buf[0] = 0x10; /* stall EPA */
+ buf[1] = 0x02; /* reset EPA */
+ }
+
+ ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
if (ret)
goto err;
@@ -586,7 +847,7 @@ err:
return ret;
}
-static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
{
int ret;
u8 gpio, sys0;
@@ -594,12 +855,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
deb_info("%s: onoff=%d\n", __func__, onoff);
/* demod adc */
- ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
+ ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0);
if (ret)
goto err;
/* tuner power, read GPIOs */
- ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
if (ret)
goto err;
@@ -619,12 +880,12 @@ static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
/* demod adc */
- ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
+ ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0);
if (ret)
goto err;
/* tuner power, write GPIOs */
- ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
if (ret)
goto err;
@@ -634,6 +895,128 @@ err:
return ret;
}
+static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 val;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ if (onoff) {
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x08;
+ val &= 0xef;
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+
+ /* demod_ctl_1 */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ if (ret)
+ goto err;
+
+ val &= 0xef;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+ if (ret)
+ goto err;
+
+ /* demod control */
+ /* PLL enable */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ /* bit 7 to 1 */
+ val |= 0x80;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ /* demod HW reset */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+ /* bit 5 to 0 */
+ val &= 0xdf;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x20;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ mdelay(5);
+
+ /*enable ADC_Q and ADC_I */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x48;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+
+ } else {
+ /* demod_ctl_1 */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x0c;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+ if (ret)
+ goto err;
+
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x10;
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+
+ /* demod control */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val &= 0x37;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+
static int rtl2831u_rc_query(struct dvb_usb_device *d)
{
int ret, i;
@@ -660,7 +1043,7 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d)
/* init remote controller */
if (!priv->rc_active) {
for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
- ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
rc_nec_tab[i].val);
if (ret)
goto err;
@@ -690,12 +1073,12 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d)
rc_keydown(d->rc_dev, rc_code, 0);
- ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
if (ret)
goto err;
/* repeated intentionally to avoid extra keypress */
- ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
if (ret)
goto err;
}
@@ -732,7 +1115,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
/* init remote controller */
if (!priv->rc_active) {
for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
- ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
rc_nec_tab[i].val);
if (ret)
goto err;
@@ -740,14 +1123,14 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
priv->rc_active = true;
}
- ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
+ ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]);
if (ret)
goto err;
if (buf[0] != 0x83)
goto exit;
- ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
+ ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]);
if (ret)
goto err;
@@ -756,9 +1139,9 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d)
/* TODO: pass raw IR to Kernel IR decoder */
- ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
- ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
- ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
+ ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03);
+ ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+ ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80);
exit:
return ret;
@@ -771,6 +1154,9 @@ enum rtl28xxu_usb_table_entry {
RTL2831U_0BDA_2831,
RTL2831U_14AA_0160,
RTL2831U_14AA_0161,
+ RTL2832U_0CCD_00A9,
+ RTL2832U_1F4D_B803,
+ RTL2832U_0CCD_00B3,
};
static struct usb_device_id rtl28xxu_table[] = {
@@ -783,6 +1169,12 @@ static struct usb_device_id rtl28xxu_table[] = {
USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
/* RTL2832U */
+ [RTL2832U_0CCD_00A9] = {
+ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1)},
+ [RTL2832U_1F4D_B803] = {
+ USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT)},
+ [RTL2832U_0CCD_00B3] = {
+ USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK)},
{} /* terminating entry */
};
@@ -805,7 +1197,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
{
.frontend_attach = rtl2831u_frontend_attach,
.tuner_attach = rtl2831u_tuner_attach,
- .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .streaming_ctrl = rtl2831u_streaming_ctrl,
.stream = {
.type = USB_BULK,
.count = 6,
@@ -821,7 +1213,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
}
},
- .power_ctrl = rtl28xxu_power_ctrl,
+ .power_ctrl = rtl2831u_power_ctrl,
.rc.core = {
.protocol = RC_TYPE_NEC,
@@ -867,7 +1259,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
{
.frontend_attach = rtl2832u_frontend_attach,
.tuner_attach = rtl2832u_tuner_attach,
- .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .streaming_ctrl = rtl2832u_streaming_ctrl,
.stream = {
.type = USB_BULK,
.count = 6,
@@ -883,7 +1275,7 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
}
},
- .power_ctrl = rtl28xxu_power_ctrl,
+ .power_ctrl = rtl2832u_power_ctrl,
.rc.core = {
.protocol = RC_TYPE_NEC,
@@ -896,10 +1288,25 @@ static struct dvb_usb_device_properties rtl28xxu_properties[] = {
.i2c_algo = &rtl28xxu_i2c_algo,
- .num_device_descs = 0, /* disabled as no support for RTL2832 */
+ .num_device_descs = 3,
.devices = {
{
- .name = "Realtek RTL2832U reference design",
+ .name = "Terratec Cinergy T Stick Black",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2832U_0CCD_00A9],
+ },
+ },
+ {
+ .name = "G-Tek Electronics Group Lifeview LV5TDLX DVB-T",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2832U_1F4D_B803],
+ },
+ },
+ {
+ .name = "NOXON DAB/DAB+ USB dongle",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2832U_0CCD_00B3],
+ },
},
}
},
@@ -910,6 +1317,7 @@ static int rtl28xxu_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret, i;
+ u8 val;
int properties_count = ARRAY_SIZE(rtl28xxu_properties);
struct dvb_usb_device *d;
struct usb_device *udev;
@@ -954,16 +1362,25 @@ static int rtl28xxu_probe(struct usb_interface *intf,
if (ret)
goto err;
+
/* init USB endpoints */
- ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
+ ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val);
+ if (ret)
+ goto err;
+
+ /* enable DMA and Full Packet Mode*/
+ val |= 0x09;
+ ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val);
if (ret)
goto err;
- ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+ /* set EPA maximum packet size to 0x0200 */
+ ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
if (ret)
goto err;
- ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+ /* change EPA FIFO length */
+ ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
if (ret)
goto err;
@@ -1007,4 +1424,5 @@ module_exit(rtl28xxu_module_exit);
MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_AUTHOR("Thomas Mair <thomas.mair86@googlemail.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index b98ebb264e29..a08c2152d0ee 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -1,6 +1,7 @@
config DVB_FE_CUSTOMISE
bool "Customise the frontend modules to build"
depends on DVB_CORE
+ depends on EXPERT
default y if EXPERT
help
This allows the user to select/deselect frontend drivers for their
@@ -432,6 +433,13 @@ config DVB_RTL2830
help
Say Y when you want to support this frontend.
+config DVB_RTL2832
+ tristate "Realtek RTL2832 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index cd1ac2fd5774..185bb8b51952 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
obj-$(CONFIG_DVB_AF9033) += af9033.o
diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c
index bb56497e940a..cff44a389b40 100644
--- a/drivers/media/dvb/frontends/a8293.c
+++ b/drivers/media/dvb/frontends/a8293.c
@@ -21,24 +21,6 @@
#include "dvb_frontend.h"
#include "a8293.h"
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-#define LOG_PREFIX "a8293"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-
struct a8293_priv {
struct i2c_adapter *i2c;
const struct a8293_config *cfg;
@@ -65,7 +47,8 @@ static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd)
if (ret == 1) {
ret = 0;
} else {
- warn("i2c failed=%d rd=%d", ret, rd);
+ dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n",
+ KBUILD_MODNAME, ret, rd);
ret = -EREMOTEIO;
}
@@ -88,7 +71,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
struct a8293_priv *priv = fe->sec_priv;
int ret;
- dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage);
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__,
+ fe_sec_voltage);
switch (fe_sec_voltage) {
case SEC_VOLTAGE_OFF:
@@ -114,14 +98,12 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
return ret;
err:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
static void a8293_release_sec(struct dvb_frontend *fe)
{
- dbg("%s:", __func__);
-
a8293_set_voltage(fe, SEC_VOLTAGE_OFF);
kfree(fe->sec_priv);
@@ -154,7 +136,7 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
/* ENB=0 */
priv->reg[0] = 0x10;
- ret = a8293_wr(priv, &priv->reg[1], 1);
+ ret = a8293_wr(priv, &priv->reg[0], 1);
if (ret)
goto err;
@@ -164,16 +146,17 @@ struct dvb_frontend *a8293_attach(struct dvb_frontend *fe,
if (ret)
goto err;
- info("Allegro A8293 SEC attached.");
-
fe->ops.release_sec = a8293_release_sec;
/* override frontend ops */
fe->ops.set_voltage = a8293_set_voltage;
+ dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n",
+ KBUILD_MODNAME);
+
return fe;
err:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
kfree(priv);
return NULL;
}
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 9ca34f495009..1f3bcb5a1de8 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -2680,12 +2680,14 @@ static int dib8000_tune(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
int ret = 0;
- u16 lock, value, mode = fft_to_mode(state);
+ u16 lock, value, mode;
// we are already tuned - just resuming from suspend
if (state == NULL)
return -EINVAL;
+ mode = fft_to_mode(state);
+
dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000);
dib8000_set_channel(state, 0, 0);
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
index 9d64e4fea066..d615d7d055a2 100644
--- a/drivers/media/dvb/frontends/drxk.h
+++ b/drivers/media/dvb/frontends/drxk.h
@@ -20,6 +20,14 @@
* means that 1=DVBC, 0 = DVBT. Zero means the opposite.
* @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
* @microcode_name: Name of the firmware file with the microcode
+ * @qam_demod_parameter_count: The number of parameters used for the command
+ * to set the demodulator parameters. All
+ * firmwares are using the 2-parameter commmand.
+ * An exception is the "drxk_a3.mc" firmware,
+ * which uses the 4-parameter command.
+ * A value of 0 (default) or lower indicates that
+ * the correct number of parameters will be
+ * automatically detected.
*
* On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
* UIO-3.
@@ -38,7 +46,8 @@ struct drxk_config {
u8 mpeg_out_clk_strength;
int chunk_size;
- const char *microcode_name;
+ const char *microcode_name;
+ int qam_demod_parameter_count;
};
#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
index 60b868faeacf..1ab8154542da 100644
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
+#include <linux/hardirq.h>
#include <asm/div64.h>
#include "dvb_frontend.h"
@@ -308,16 +309,42 @@ static u32 Log10Times100(u32 x)
/* I2C **********************************************************************/
/****************************************************************************/
-static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val)
+static int drxk_i2c_lock(struct drxk_state *state)
+{
+ i2c_lock_adapter(state->i2c);
+ state->drxk_i2c_exclusive_lock = true;
+
+ return 0;
+}
+
+static void drxk_i2c_unlock(struct drxk_state *state)
+{
+ if (!state->drxk_i2c_exclusive_lock)
+ return;
+
+ i2c_unlock_adapter(state->i2c);
+ state->drxk_i2c_exclusive_lock = false;
+}
+
+static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
+ unsigned len)
+{
+ if (state->drxk_i2c_exclusive_lock)
+ return __i2c_transfer(state->i2c, msgs, len);
+ else
+ return i2c_transfer(state->i2c, msgs, len);
+}
+
+static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
{
struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
.buf = val, .len = 1}
};
- return i2c_transfer(adapter, msgs, 1);
+ return drxk_i2c_transfer(state, msgs, 1);
}
-static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len)
{
int status;
struct i2c_msg msg = {
@@ -330,7 +357,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
printk(KERN_CONT " %02x", data[i]);
printk(KERN_CONT "\n");
}
- status = i2c_transfer(adap, &msg, 1);
+ status = drxk_i2c_transfer(state, &msg, 1);
if (status >= 0 && status != 1)
status = -EIO;
@@ -340,7 +367,7 @@ static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
return status;
}
-static int i2c_read(struct i2c_adapter *adap,
+static int i2c_read(struct drxk_state *state,
u8 adr, u8 *msg, int len, u8 *answ, int alen)
{
int status;
@@ -351,7 +378,7 @@ static int i2c_read(struct i2c_adapter *adap,
.buf = answ, .len = alen}
};
- status = i2c_transfer(adap, msgs, 2);
+ status = drxk_i2c_transfer(state, msgs, 2);
if (status != 2) {
if (debug > 2)
printk(KERN_CONT ": ERROR!\n");
@@ -394,7 +421,7 @@ static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
len = 2;
}
dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
- status = i2c_read(state->i2c, adr, mm1, len, mm2, 2);
+ status = i2c_read(state, adr, mm1, len, mm2, 2);
if (status < 0)
return status;
if (data)
@@ -428,7 +455,7 @@ static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
len = 2;
}
dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
- status = i2c_read(state->i2c, adr, mm1, len, mm2, 4);
+ status = i2c_read(state, adr, mm1, len, mm2, 4);
if (status < 0)
return status;
if (data)
@@ -464,7 +491,7 @@ static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
mm[len + 1] = (data >> 8) & 0xff;
dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
- return i2c_write(state->i2c, adr, mm, len + 2);
+ return i2c_write(state, adr, mm, len + 2);
}
static int write16(struct drxk_state *state, u32 reg, u16 data)
@@ -495,7 +522,7 @@ static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
mm[len + 3] = (data >> 24) & 0xff;
dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
- return i2c_write(state->i2c, adr, mm, len + 4);
+ return i2c_write(state, adr, mm, len + 4);
}
static int write32(struct drxk_state *state, u32 reg, u32 data)
@@ -542,7 +569,7 @@ static int write_block(struct drxk_state *state, u32 Address,
printk(KERN_CONT " %02x", pBlock[i]);
printk(KERN_CONT "\n");
}
- status = i2c_write(state->i2c, state->demod_address,
+ status = i2c_write(state, state->demod_address,
&state->Chunk[0], Chunk + AdrLength);
if (status < 0) {
printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
@@ -568,17 +595,17 @@ int PowerUpDevice(struct drxk_state *state)
dprintk(1, "\n");
- status = i2c_read1(state->i2c, state->demod_address, &data);
+ status = i2c_read1(state, state->demod_address, &data);
if (status < 0) {
do {
data = 0;
- status = i2c_write(state->i2c, state->demod_address,
+ status = i2c_write(state, state->demod_address,
&data, 1);
msleep(10);
retryCount++;
if (status < 0)
continue;
- status = i2c_read1(state->i2c, state->demod_address,
+ status = i2c_read1(state, state->demod_address,
&data);
} while (status < 0 &&
(retryCount < DRXK_MAX_RETRIES_POWERUP));
@@ -932,7 +959,7 @@ static int GetDeviceCapabilities(struct drxk_state *state)
if (status < 0)
goto error;
-printk(KERN_ERR "drxk: status = 0x%08x\n", sioTopJtagidLo);
+ printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo);
/* driver 0.9.0 */
switch ((sioTopJtagidLo >> 29) & 0xF) {
@@ -2824,7 +2851,7 @@ static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
dprintk(1, "\n");
if (state->m_DrxkState == DRXK_UNINITIALIZED)
- goto error;
+ return 0;
if (state->m_DrxkState == DRXK_POWERED_DOWN)
goto error;
@@ -2977,7 +3004,7 @@ static int ADCSynchronization(struct drxk_state *state)
status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
if (status < 0)
goto error;
- if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+ if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
clkNeg |=
@@ -5361,7 +5388,7 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
Result);
if (status < 0)
- printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status);
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
/* 0x0000 NOT LOCKED */
@@ -5388,12 +5415,67 @@ static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
#define QAM_LOCKRANGE__M 0x10
#define QAM_LOCKRANGE_NORMAL 0x10
+static int QAMDemodulatorCommand(struct drxk_state *state,
+ int numberOfParameters)
+{
+ int status;
+ u16 cmdResult;
+ u16 setParamParameters[4] = { 0, 0, 0, 0 };
+
+ setParamParameters[0] = state->m_Constellation; /* modulation */
+ setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+
+ if (numberOfParameters == 2) {
+ u16 setEnvParameters[1] = { 0 };
+
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setEnvParameters[0] = QAM_TOP_ANNEX_C;
+ else
+ setEnvParameters[0] = QAM_TOP_ANNEX_A;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 1, setEnvParameters, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ numberOfParameters, setParamParameters,
+ 1, &cmdResult);
+ } else if (numberOfParameters == 4) {
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setParamParameters[2] = QAM_TOP_ANNEX_C;
+ else
+ setParamParameters[2] = QAM_TOP_ANNEX_A;
+
+ setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+ /* Env parameters */
+ /* check for LOCKRANGE Extented */
+ /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ numberOfParameters, setParamParameters,
+ 1, &cmdResult);
+ } else {
+ printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
+ "count %d\n", numberOfParameters);
+ }
+
+error:
+ if (status < 0)
+ printk(KERN_WARNING "drxk: Warning %d on %s\n",
+ status, __func__);
+ return status;
+}
+
static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
s32 tunerFreqOffset)
{
int status;
- u16 setParamParameters[4] = { 0, 0, 0, 0 };
u16 cmdResult;
+ int qamDemodParamCount = state->qam_demod_parameter_count;
dprintk(1, "\n");
/*
@@ -5445,34 +5527,42 @@ static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
}
if (status < 0)
goto error;
- setParamParameters[0] = state->m_Constellation; /* modulation */
- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setParamParameters[2] = QAM_TOP_ANNEX_C;
- else
- setParamParameters[2] = QAM_TOP_ANNEX_A;
- setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
- /* Env parameters */
- /* check for LOCKRANGE Extented */
- /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult);
- if (status < 0) {
- /* Fall-back to the simpler call */
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setParamParameters[0] = QAM_TOP_ANNEX_C;
- else
- setParamParameters[0] = QAM_TOP_ANNEX_A;
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult);
- if (status < 0)
- goto error;
+ /* Use the 4-parameter if it's requested or we're probing for
+ * the correct command. */
+ if (state->qam_demod_parameter_count == 4
+ || !state->qam_demod_parameter_count) {
+ qamDemodParamCount = 4;
+ status = QAMDemodulatorCommand(state, qamDemodParamCount);
+ }
- setParamParameters[0] = state->m_Constellation; /* modulation */
- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult);
+ /* Use the 2-parameter command if it was requested or if we're
+ * probing for the correct command and the 4-parameter command
+ * failed. */
+ if (state->qam_demod_parameter_count == 2
+ || (!state->qam_demod_parameter_count && status < 0)) {
+ qamDemodParamCount = 2;
+ status = QAMDemodulatorCommand(state, qamDemodParamCount);
}
- if (status < 0)
+
+ if (status < 0) {
+ dprintk(1, "Could not set demodulator parameters. Make "
+ "sure qam_demod_parameter_count (%d) is correct for "
+ "your firmware (%s).\n",
+ state->qam_demod_parameter_count,
+ state->microcode_name);
goto error;
+ } else if (!state->qam_demod_parameter_count) {
+ dprintk(1, "Auto-probing the correct QAM demodulator command "
+ "parameters was successful - using %d parameters.\n",
+ qamDemodParamCount);
+
+ /*
+ * One of our commands was successful. We don't need to
+ * auto-probe anymore, now that we got the correct command.
+ */
+ state->qam_demod_parameter_count = qamDemodParamCount;
+ }
/*
* STEP 3: enable the system in a mode where the ADC provides valid
@@ -5968,34 +6058,15 @@ error:
return status;
}
-static int load_microcode(struct drxk_state *state, const char *mc_name)
-{
- const struct firmware *fw = NULL;
- int err = 0;
-
- dprintk(1, "\n");
-
- err = request_firmware(&fw, mc_name, state->i2c->dev.parent);
- if (err < 0) {
- printk(KERN_ERR
- "drxk: Could not load firmware file %s.\n", mc_name);
- printk(KERN_INFO
- "drxk: Copy %s to your hotplug directory!\n", mc_name);
- return err;
- }
- err = DownloadMicrocode(state, fw->data, fw->size);
- release_firmware(fw);
- return err;
-}
-
static int init_drxk(struct drxk_state *state)
{
- int status = 0;
+ int status = 0, n = 0;
enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
u16 driverVersion;
dprintk(1, "\n");
if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+ drxk_i2c_lock(state);
status = PowerUpDevice(state);
if (status < 0)
goto error;
@@ -6073,8 +6144,12 @@ static int init_drxk(struct drxk_state *state)
if (status < 0)
goto error;
- if (state->microcode_name)
- load_microcode(state, state->microcode_name);
+ if (state->fw) {
+ status = DownloadMicrocode(state, state->fw->data,
+ state->fw->size);
+ if (status < 0)
+ goto error;
+ }
/* disable token-ring bus through OFDM block for possible ucode upload */
status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
@@ -6167,19 +6242,71 @@ static int init_drxk(struct drxk_state *state)
state->m_DrxkState = DRXK_POWERED_DOWN;
} else
state->m_DrxkState = DRXK_STOPPED;
+
+ /* Initialize the supported delivery systems */
+ n = 0;
+ if (state->m_hasDVBC) {
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
+ strlcat(state->frontend.ops.info.name, " DVB-C",
+ sizeof(state->frontend.ops.info.name));
+ }
+ if (state->m_hasDVBT) {
+ state->frontend.ops.delsys[n++] = SYS_DVBT;
+ strlcat(state->frontend.ops.info.name, " DVB-T",
+ sizeof(state->frontend.ops.info.name));
+ }
+ drxk_i2c_unlock(state);
}
error:
- if (status < 0)
+ if (status < 0) {
+ state->m_DrxkState = DRXK_NO_DEV;
+ drxk_i2c_unlock(state);
printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ }
return status;
}
+static void load_firmware_cb(const struct firmware *fw,
+ void *context)
+{
+ struct drxk_state *state = context;
+
+ dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
+ if (!fw) {
+ printk(KERN_ERR
+ "drxk: Could not load firmware file %s.\n",
+ state->microcode_name);
+ printk(KERN_INFO
+ "drxk: Copy %s to your hotplug directory!\n",
+ state->microcode_name);
+ state->microcode_name = NULL;
+
+ /*
+ * As firmware is now load asynchronous, it is not possible
+ * anymore to fail at frontend attach. We might silently
+ * return here, and hope that the driver won't crash.
+ * We might also change all DVB callbacks to return -ENODEV
+ * if the device is not initialized.
+ * As the DRX-K devices have their own internal firmware,
+ * let's just hope that it will match a firmware revision
+ * compatible with this driver and proceed.
+ */
+ }
+ state->fw = fw;
+
+ init_drxk(state);
+}
+
static void drxk_release(struct dvb_frontend *fe)
{
struct drxk_state *state = fe->demodulator_priv;
dprintk(1, "\n");
+ if (state->fw)
+ release_firmware(state->fw);
+
kfree(state);
}
@@ -6188,6 +6315,12 @@ static int drxk_sleep(struct dvb_frontend *fe)
struct drxk_state *state = fe->demodulator_priv;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return 0;
+
ShutDown(state);
return 0;
}
@@ -6196,7 +6329,11 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct drxk_state *state = fe->demodulator_priv;
- dprintk(1, "%s\n", enable ? "enable" : "disable");
+ dprintk(1, ": %s\n", enable ? "enable" : "disable");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+
return ConfigureI2CBridge(state, enable ? true : false);
}
@@ -6209,6 +6346,12 @@ static int drxk_set_parameters(struct dvb_frontend *fe)
dprintk(1, "\n");
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
if (!fe->ops.tuner_ops.get_if_frequency) {
printk(KERN_ERR
"drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
@@ -6262,6 +6405,12 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
u32 stat;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
*status = 0;
GetLockStatus(state, &stat, 0);
if (stat == MPEG_LOCK)
@@ -6275,8 +6424,15 @@ static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
{
+ struct drxk_state *state = fe->demodulator_priv;
+
dprintk(1, "\n");
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
*ber = 0;
return 0;
}
@@ -6288,6 +6444,12 @@ static int drxk_read_signal_strength(struct dvb_frontend *fe,
u32 val = 0;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
ReadIFAgc(state, &val);
*strength = val & 0xffff;
return 0;
@@ -6299,6 +6461,12 @@ static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
s32 snr2;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
GetSignalToNoise(state, &snr2);
*snr = snr2 & 0xffff;
return 0;
@@ -6310,6 +6478,12 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
u16 err;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
DVBTQAMGetAccPktErr(state, &err);
*ucblocks = (u32) err;
return 0;
@@ -6318,9 +6492,16 @@ static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
*sets)
{
+ struct drxk_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
switch (p->delivery_system) {
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
@@ -6371,10 +6552,9 @@ static struct dvb_frontend_ops drxk_ops = {
struct dvb_frontend *drxk_attach(const struct drxk_config *config,
struct i2c_adapter *i2c)
{
- int n;
-
struct drxk_state *state = NULL;
u8 adr = config->adr;
+ int status;
dprintk(1, "\n");
state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
@@ -6385,6 +6565,7 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
state->demod_address = adr;
state->single_master = config->single_master;
state->microcode_name = config->microcode_name;
+ state->qam_demod_parameter_count = config->qam_demod_parameter_count;
state->no_i2c_bridge = config->no_i2c_bridge;
state->antenna_gpio = config->antenna_gpio;
state->antenna_dvbt = config->antenna_dvbt;
@@ -6425,22 +6606,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
state->frontend.demodulator_priv = state;
init_state(state);
- if (init_drxk(state) < 0)
- goto error;
- /* Initialize the supported delivery systems */
- n = 0;
- if (state->m_hasDVBC) {
- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
- strlcat(state->frontend.ops.info.name, " DVB-C",
- sizeof(state->frontend.ops.info.name));
- }
- if (state->m_hasDVBT) {
- state->frontend.ops.delsys[n++] = SYS_DVBT;
- strlcat(state->frontend.ops.info.name, " DVB-T",
- sizeof(state->frontend.ops.info.name));
- }
+ /* Load firmware and initialize DRX-K */
+ if (state->microcode_name) {
+ status = request_firmware_nowait(THIS_MODULE, 1,
+ state->microcode_name,
+ state->i2c->dev.parent,
+ GFP_KERNEL,
+ state, load_firmware_cb);
+ if (status < 0) {
+ printk(KERN_ERR
+ "drxk: failed to request a firmware\n");
+ return NULL;
+ }
+ } else if (init_drxk(state) < 0)
+ goto error;
printk(KERN_INFO "drxk: frontend initialized.\n");
return &state->frontend;
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h
index 4bbf841de83a..6bb9fc4a7b96 100644
--- a/drivers/media/dvb/frontends/drxk_hard.h
+++ b/drivers/media/dvb/frontends/drxk_hard.h
@@ -94,7 +94,15 @@ enum DRXPowerMode {
enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF };
-enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN };
+enum EDrxkState {
+ DRXK_UNINITIALIZED = 0,
+ DRXK_STOPPED,
+ DRXK_DTV_STARTED,
+ DRXK_ATV_STARTED,
+ DRXK_POWERED_DOWN,
+ DRXK_NO_DEV /* If drxk init failed */
+};
+
enum EDrxkCoefArrayIndex {
DRXK_COEF_IDX_MN = 0,
DRXK_COEF_IDX_FM ,
@@ -325,6 +333,9 @@ struct drxk_state {
enum DRXPowerMode m_currentPowerMode;
+ /* when true, avoids other devices to use the I2C bus */
+ bool drxk_i2c_exclusive_lock;
+
/*
* Configurable parameters at the driver. They stores the values found
* at struct drxk_config.
@@ -338,7 +349,11 @@ struct drxk_state {
bool antenna_dvbt;
u16 antenna_gpio;
+ /* Firmware */
const char *microcode_name;
+ struct completion fw_wait_load;
+ const struct firmware *fw;
+ int qam_demod_parameter_count;
};
#define NEVER_LOCK 0
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c
index 568363a10a31..c2ea2749ebed 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ b/drivers/media/dvb/frontends/lgs8gxx.c
@@ -40,6 +40,8 @@
static int debug;
static int fake_signal_str = 1;
+#define LGS8GXX_FIRMWARE "lgs8g75.fw"
+
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
@@ -592,7 +594,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv)
int rc;
int i;
- rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
+ rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
if (rc)
return rc;
@@ -1070,3 +1072,4 @@ EXPORT_SYMBOL(lgs8gxx_attach);
MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
diff --git a/drivers/media/dvb/frontends/rtl2832.c b/drivers/media/dvb/frontends/rtl2832.c
new file mode 100644
index 000000000000..28269ccaeab7
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2832.c
@@ -0,0 +1,789 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.
+ *
+ * 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 "rtl2832_priv.h"
+#include <linux/bitops.h>
+
+int rtl2832_debug;
+module_param_named(debug, rtl2832_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+#define REG_MASK(b) (BIT(b + 1) - 1)
+
+static const struct rtl2832_reg_entry registers[] = {
+ [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2},
+ [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3},
+ [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2},
+ [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0},
+ [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7},
+ [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7},
+ [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6},
+ [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0},
+ [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0},
+ [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0},
+ [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0},
+ [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0},
+ [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0},
+ [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0},
+ [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0},
+ [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0},
+ [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4},
+ [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0},
+ [DVBT_REG_PI] = {0x0, 0xa, 2, 0},
+ [DVBT_PIP_ON] = {0x0, 0x21, 3, 3},
+ [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0},
+ [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0},
+ [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0},
+ [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0},
+ [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0},
+ [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0},
+ [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0},
+ [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0},
+ [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0},
+ [DVBT_KB_P1] = {0x1, 0x64, 3, 1},
+ [DVBT_KB_P2] = {0x1, 0x64, 6, 4},
+ [DVBT_KB_P3] = {0x1, 0x65, 2, 0},
+ [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4},
+ [DVBT_AD_AVI] = {0x0, 0x9, 1, 0},
+ [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2},
+ [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4},
+ [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0},
+ [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3},
+ [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0},
+ [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3},
+ [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0},
+ [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6},
+ [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0},
+ [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0},
+ [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2},
+ [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4},
+ [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3},
+ [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2},
+ [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4},
+ [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0},
+ [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3},
+ [DVBT_GI_IDX] = {0x3, 0x51, 1, 0},
+ [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2},
+ [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0},
+ [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0},
+ [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0},
+ [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0},
+ [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0},
+ [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0},
+ [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0},
+ [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1},
+ [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0},
+ [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5},
+ [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6},
+ [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7},
+ [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0},
+ [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0},
+ [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0},
+ [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0},
+ [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6},
+ [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0},
+ [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6},
+ [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0},
+ [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0},
+ [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0},
+ [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0},
+ [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1},
+ [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1},
+ [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7},
+ [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0},
+ [DVBT_VTOP1] = {0x1, 0x6, 5, 0},
+ [DVBT_VTOP2] = {0x1, 0xc9, 5, 0},
+ [DVBT_VTOP3] = {0x1, 0xca, 5, 0},
+ [DVBT_KRF1] = {0x1, 0xcb, 7, 0},
+ [DVBT_KRF2] = {0x1, 0x7, 7, 0},
+ [DVBT_KRF3] = {0x1, 0xcd, 7, 0},
+ [DVBT_KRF4] = {0x1, 0xce, 7, 0},
+ [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0},
+ [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0},
+ [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0},
+ [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0},
+ [DVBT_THD_DW1] = {0x1, 0xde, 7, 0},
+ [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0},
+ [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3},
+ [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0},
+ [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5},
+ [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6},
+ [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7},
+ [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0},
+ [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1},
+ [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2},
+ [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3},
+ [DVBT_SERIAL] = {0x1, 0x7c, 4, 4},
+ [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5},
+ [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0},
+ [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4},
+ [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7},
+ [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6},
+ [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4},
+ [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3},
+ [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2},
+ [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1},
+ [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0},
+ [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4},
+ [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3},
+ [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2},
+ [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1},
+ [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0},
+ [DVBT_SM_PASS] = {0x1, 0x93, 11, 0},
+ [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0},
+ [DVBT_RSSI_R] = {0x3, 0x1, 6, 0},
+ [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0},
+ [DVBT_REG_MON] = {0x0, 0xd, 1, 0},
+ [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2},
+ [DVBT_REG_GPE] = {0x0, 0xd, 7, 7},
+ [DVBT_REG_GPO] = {0x0, 0x10, 0, 0},
+ [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0},
+};
+
+/* write multiple hardware registers */
+static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ 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 = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+}
+return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+ int len)
+{
+ int ret;
+
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2832_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+}
+
+return rtl2832_wr(priv, reg, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+ int len)
+{
+ int ret;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2832_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2832_rd(priv, reg, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
+{
+ return rtl2832_wr_regs(priv, reg, page, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
+{
+ return rtl2832_rd_regs(priv, reg, page, val, 1);
+}
+
+int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
+{
+ int ret;
+
+ u8 reg_start_addr;
+ u8 msb, lsb;
+ u8 page;
+ u8 reading[4];
+ u32 reading_tmp;
+ int i;
+
+ u8 len;
+ u32 mask;
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ page = registers[reg].page;
+
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ *val = (reading_tmp >> lsb) & mask;
+
+ return ret;
+
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+
+}
+
+int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
+{
+ int ret, i;
+ u8 len;
+ u8 reg_start_addr;
+ u8 msb, lsb;
+ u8 page;
+ u32 mask;
+
+
+ u8 reading[4];
+ u8 writing[4];
+ u32 reading_tmp;
+ u32 writing_tmp;
+
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ page = registers[reg].page;
+
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+
+ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ writing_tmp = reading_tmp & ~(mask << lsb);
+ writing_tmp |= ((val & mask) << lsb);
+
+
+ for (i = 0; i < len; i++)
+ writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
+
+ ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len);
+ if (ret)
+ goto err;
+
+ return ret;
+
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+
+}
+
+
+static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dbg("%s: enable=%d", __func__, enable);
+
+ /* gate already open or close */
+ if (priv->i2c_gate_state == enable)
+ return 0;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0));
+ if (ret)
+ goto err;
+
+ priv->i2c_gate_state = enable;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+
+
+static int rtl2832_init(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int i, ret;
+
+ u8 en_bbin;
+ u64 pset_iffreq;
+
+ /* initialization values for the demodulator registers */
+ struct rtl2832_reg_value rtl2832_initial_regs[] = {
+ {DVBT_AD_EN_REG, 0x1},
+ {DVBT_AD_EN_REG1, 0x1},
+ {DVBT_RSD_BER_FAIL_VAL, 0x2800},
+ {DVBT_MGD_THD0, 0x10},
+ {DVBT_MGD_THD1, 0x20},
+ {DVBT_MGD_THD2, 0x20},
+ {DVBT_MGD_THD3, 0x40},
+ {DVBT_MGD_THD4, 0x22},
+ {DVBT_MGD_THD5, 0x32},
+ {DVBT_MGD_THD6, 0x37},
+ {DVBT_MGD_THD7, 0x39},
+ {DVBT_EN_BK_TRK, 0x0},
+ {DVBT_EN_CACQ_NOTCH, 0x0},
+ {DVBT_AD_AV_REF, 0x2a},
+ {DVBT_REG_PI, 0x6},
+ {DVBT_PIP_ON, 0x0},
+ {DVBT_CDIV_PH0, 0x8},
+ {DVBT_CDIV_PH1, 0x8},
+ {DVBT_SCALE1_B92, 0x4},
+ {DVBT_SCALE1_B93, 0xb0},
+ {DVBT_SCALE1_BA7, 0x78},
+ {DVBT_SCALE1_BA9, 0x28},
+ {DVBT_SCALE1_BAA, 0x59},
+ {DVBT_SCALE1_BAB, 0x83},
+ {DVBT_SCALE1_BAC, 0xd4},
+ {DVBT_SCALE1_BB0, 0x65},
+ {DVBT_SCALE1_BB1, 0x43},
+ {DVBT_KB_P1, 0x1},
+ {DVBT_KB_P2, 0x4},
+ {DVBT_KB_P3, 0x7},
+ {DVBT_K1_CR_STEP12, 0xa},
+ {DVBT_REG_GPE, 0x1},
+ {DVBT_SERIAL, 0x0},
+ {DVBT_CDIV_PH0, 0x9},
+ {DVBT_CDIV_PH1, 0x9},
+ {DVBT_MPEG_IO_OPT_2_2, 0x0},
+ {DVBT_MPEG_IO_OPT_1_0, 0x0},
+ {DVBT_TRK_KS_P2, 0x4},
+ {DVBT_TRK_KS_I2, 0x7},
+ {DVBT_TR_THD_SET2, 0x6},
+ {DVBT_TRK_KC_I2, 0x5},
+ {DVBT_CR_THD_SET2, 0x1},
+ {DVBT_SPEC_INV, 0x0},
+ {DVBT_DAGC_TRG_VAL, 0x5a},
+ {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, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9bf},
+ {DVBT_EN_GI_PGA, 0x0},
+ {DVBT_THD_LOCK_UP, 0x0},
+ {DVBT_THD_LOCK_DW, 0x0},
+ {DVBT_THD_UP1, 0x11},
+ {DVBT_THD_DW1, 0xef},
+ {DVBT_INTER_CNT_LEN, 0xc},
+ {DVBT_GI_PGA_STATE, 0x0},
+ {DVBT_EN_AGC_PGA, 0x1},
+ {DVBT_IF_AGC_MAN, 0x0},
+ };
+
+
+ dbg("%s", __func__);
+
+ en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0);
+
+ /*
+ * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
+ * / CrystalFreqHz)
+ */
+ pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal;
+ pset_iffreq *= 0x400000;
+ pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal);
+ pset_iffreq = pset_iffreq & 0x3fffff;
+
+
+
+ for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
+ ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg,
+ rtl2832_initial_regs[i].value);
+ if (ret)
+ goto err;
+ }
+
+ /* if frequency settings */
+ ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dbg("%s", __func__);
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2832_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ dbg("%s", __func__);
+ s->min_delay_ms = 1000;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+ return 0;
+}
+
+static int rtl2832_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, j;
+ u64 bw_mode, num, num2;
+ u32 resamp_ratio, cfreq_off_ratio;
+
+
+ static u8 bw_params[3][32] = {
+ /* 6 MHz bandwidth */
+ {
+ 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f,
+ 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2,
+ 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67,
+ 0x19, 0xe0,
+ },
+
+ /* 7 MHz bandwidth */
+ {
+ 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf,
+ 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30,
+ 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22,
+ 0x19, 0x10,
+ },
+
+ /* 8 MHz bandwidth */
+ {
+ 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf,
+ 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7,
+ 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8,
+ 0x19, 0xe0,
+ },
+ };
+
+
+ dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ bw_mode = 48000000;
+ break;
+ case 7000000:
+ i = 1;
+ bw_mode = 56000000;
+ break;
+ case 8000000:
+ i = 2;
+ bw_mode = 64000000;
+ break;
+ default:
+ dbg("invalid bandwidth");
+ return -EINVAL;
+ }
+
+ for (j = 0; j < sizeof(bw_params[0]); j++) {
+ ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
+ if (ret)
+ goto err;
+ }
+
+ /* calculate and set resample ratio
+ * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
+ * / ConstWithBandwidthMode)
+ */
+ num = priv->cfg.xtal * 7;
+ num *= 0x400000;
+ num = div_u64(num, bw_mode);
+ resamp_ratio = num & 0x3ffffff;
+ ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio);
+ if (ret)
+ goto err;
+
+ /* calculate and set cfreq off ratio
+ * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20)
+ * / (CrystalFreqHz * 7))
+ */
+ num = bw_mode << 20;
+ num2 = priv->cfg.xtal * 7;
+ num = div_u64(num, num2);
+ num = -num;
+ cfreq_off_ratio = num & 0xfffff;
+ ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
+ if (ret)
+ goto err;
+
+
+ /* soft reset */
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ info("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int ret;
+ u32 tmp;
+ *status = 0;
+
+
+ dbg("%s", __func__);
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ /* TODO find out if this is also true for rtl2832? */
+ /*else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }*/
+
+ return ret;
+err:
+ info("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int rtl2832_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+
+static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops rtl2832_ops;
+
+static void rtl2832_release(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dbg("%s", __func__);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2832_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ dbg("%s", __func__);
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ priv->tuner = cfg->tuner;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
+
+ /* check if the demod is there */
+ ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* TODO implement sleep mode */
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2832_attach);
+
+static struct dvb_frontend_ops rtl2832_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2832 (DVB-T)",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2832_release,
+
+ .init = rtl2832_init,
+ .sleep = rtl2832_sleep,
+
+ .get_tune_settings = rtl2832_get_tune_settings,
+
+ .set_frontend = rtl2832_set_frontend,
+
+ .read_status = rtl2832_read_status,
+ .read_snr = rtl2832_read_snr,
+ .read_ber = rtl2832_read_ber,
+ .read_ucblocks = rtl2832_read_ucblocks,
+ .read_signal_strength = rtl2832_read_signal_strength,
+ .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
+MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/frontends/rtl2832.h b/drivers/media/dvb/frontends/rtl2832.h
new file mode 100644
index 000000000000..d94dc9a3fa62
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2832.h
@@ -0,0 +1,74 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.
+ *
+ * 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 RTL2832_H
+#define RTL2832_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2832_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * IFs for all used modes.
+ * Hz
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+ */
+ u32 if_dvbt;
+
+ /*
+ */
+ u8 tuner;
+};
+
+
+#if defined(CONFIG_DVB_RTL2832) || \
+ (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2832_attach(
+ const struct rtl2832_config *cfg,
+ struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2832_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2832_attach(
+ const struct rtl2832_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+
+#endif /* RTL2832_H */
diff --git a/drivers/media/dvb/frontends/rtl2832_priv.h b/drivers/media/dvb/frontends/rtl2832_priv.h
new file mode 100644
index 000000000000..0ce9502da8ba
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2832_priv.h
@@ -0,0 +1,260 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@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.
+ *
+ * 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 RTL2832_PRIV_H
+#define RTL2832_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2832.h"
+
+#define LOG_PREFIX "rtl2832"
+
+#undef dbg
+#define dbg(f, arg...) \
+do { \
+ if (rtl2832_debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg); \
+} while (0)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct rtl2832_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2832_config cfg;
+
+ bool i2c_gate_state;
+ bool sleeping;
+
+ u8 tuner;
+ u8 page; /* active register page */
+};
+
+struct rtl2832_reg_entry {
+ u8 page;
+ u8 start_address;
+ u8 msb;
+ u8 lsb;
+};
+
+struct rtl2832_reg_value {
+ int reg;
+ u32 value;
+};
+
+
+/* Demod register bit names */
+enum DVBT_REG_BIT_NAME {
+ DVBT_SOFT_RST,
+ DVBT_IIC_REPEAT,
+ DVBT_TR_WAIT_MIN_8K,
+ DVBT_RSD_BER_FAIL_VAL,
+ DVBT_EN_BK_TRK,
+ DVBT_REG_PI,
+ DVBT_REG_PFREQ_1_0,
+ DVBT_PD_DA8,
+ DVBT_LOCK_TH,
+ DVBT_BER_PASS_SCAL,
+ DVBT_CE_FFSM_BYPASS,
+ DVBT_ALPHAIIR_N,
+ DVBT_ALPHAIIR_DIF,
+ DVBT_EN_TRK_SPAN,
+ DVBT_LOCK_TH_LEN,
+ DVBT_CCI_THRE,
+ DVBT_CCI_MON_SCAL,
+ DVBT_CCI_M0,
+ DVBT_CCI_M1,
+ DVBT_CCI_M2,
+ DVBT_CCI_M3,
+ DVBT_SPEC_INIT_0,
+ DVBT_SPEC_INIT_1,
+ DVBT_SPEC_INIT_2,
+ DVBT_AD_EN_REG,
+ DVBT_AD_EN_REG1,
+ DVBT_EN_BBIN,
+ DVBT_MGD_THD0,
+ DVBT_MGD_THD1,
+ DVBT_MGD_THD2,
+ DVBT_MGD_THD3,
+ DVBT_MGD_THD4,
+ DVBT_MGD_THD5,
+ DVBT_MGD_THD6,
+ DVBT_MGD_THD7,
+ DVBT_EN_CACQ_NOTCH,
+ DVBT_AD_AV_REF,
+ DVBT_PIP_ON,
+ DVBT_SCALE1_B92,
+ DVBT_SCALE1_B93,
+ DVBT_SCALE1_BA7,
+ DVBT_SCALE1_BA9,
+ DVBT_SCALE1_BAA,
+ DVBT_SCALE1_BAB,
+ DVBT_SCALE1_BAC,
+ DVBT_SCALE1_BB0,
+ DVBT_SCALE1_BB1,
+ DVBT_KB_P1,
+ DVBT_KB_P2,
+ DVBT_KB_P3,
+ DVBT_OPT_ADC_IQ,
+ DVBT_AD_AVI,
+ DVBT_AD_AVQ,
+ DVBT_K1_CR_STEP12,
+ DVBT_TRK_KS_P2,
+ DVBT_TRK_KS_I2,
+ DVBT_TR_THD_SET2,
+ DVBT_TRK_KC_P2,
+ DVBT_TRK_KC_I2,
+ DVBT_CR_THD_SET2,
+ DVBT_PSET_IFFREQ,
+ DVBT_SPEC_INV,
+ DVBT_BW_INDEX,
+ DVBT_RSAMP_RATIO,
+ DVBT_CFREQ_OFF_RATIO,
+ DVBT_FSM_STAGE,
+ DVBT_RX_CONSTEL,
+ DVBT_RX_HIER,
+ DVBT_RX_C_RATE_LP,
+ DVBT_RX_C_RATE_HP,
+ DVBT_GI_IDX,
+ DVBT_FFT_MODE_IDX,
+ DVBT_RSD_BER_EST,
+ DVBT_CE_EST_EVM,
+ DVBT_RF_AGC_VAL,
+ DVBT_IF_AGC_VAL,
+ DVBT_DAGC_VAL,
+ DVBT_SFREQ_OFF,
+ DVBT_CFREQ_OFF,
+ DVBT_POLAR_RF_AGC,
+ DVBT_POLAR_IF_AGC,
+ DVBT_AAGC_HOLD,
+ DVBT_EN_RF_AGC,
+ DVBT_EN_IF_AGC,
+ DVBT_IF_AGC_MIN,
+ DVBT_IF_AGC_MAX,
+ DVBT_RF_AGC_MIN,
+ DVBT_RF_AGC_MAX,
+ DVBT_IF_AGC_MAN,
+ DVBT_IF_AGC_MAN_VAL,
+ DVBT_RF_AGC_MAN,
+ DVBT_RF_AGC_MAN_VAL,
+ DVBT_DAGC_TRG_VAL,
+ DVBT_AGC_TARG_VAL,
+ DVBT_LOOP_GAIN_3_0,
+ DVBT_LOOP_GAIN_4,
+ DVBT_VTOP,
+ DVBT_KRF,
+ DVBT_AGC_TARG_VAL_0,
+ DVBT_AGC_TARG_VAL_8_1,
+ DVBT_AAGC_LOOP_GAIN,
+ DVBT_LOOP_GAIN2_3_0,
+ DVBT_LOOP_GAIN2_4,
+ DVBT_LOOP_GAIN3,
+ DVBT_VTOP1,
+ DVBT_VTOP2,
+ DVBT_VTOP3,
+ DVBT_KRF1,
+ DVBT_KRF2,
+ DVBT_KRF3,
+ DVBT_KRF4,
+ DVBT_EN_GI_PGA,
+ DVBT_THD_LOCK_UP,
+ DVBT_THD_LOCK_DW,
+ DVBT_THD_UP1,
+ DVBT_THD_DW1,
+ DVBT_INTER_CNT_LEN,
+ DVBT_GI_PGA_STATE,
+ DVBT_EN_AGC_PGA,
+ DVBT_CKOUTPAR,
+ DVBT_CKOUT_PWR,
+ DVBT_SYNC_DUR,
+ DVBT_ERR_DUR,
+ DVBT_SYNC_LVL,
+ DVBT_ERR_LVL,
+ DVBT_VAL_LVL,
+ DVBT_SERIAL,
+ DVBT_SER_LSB,
+ DVBT_CDIV_PH0,
+ DVBT_CDIV_PH1,
+ DVBT_MPEG_IO_OPT_2_2,
+ DVBT_MPEG_IO_OPT_1_0,
+ DVBT_CKOUTPAR_PIP,
+ DVBT_CKOUT_PWR_PIP,
+ DVBT_SYNC_LVL_PIP,
+ DVBT_ERR_LVL_PIP,
+ DVBT_VAL_LVL_PIP,
+ DVBT_CKOUTPAR_PID,
+ DVBT_CKOUT_PWR_PID,
+ DVBT_SYNC_LVL_PID,
+ DVBT_ERR_LVL_PID,
+ DVBT_VAL_LVL_PID,
+ DVBT_SM_PASS,
+ DVBT_UPDATE_REG_2,
+ DVBT_BTHD_P3,
+ DVBT_BTHD_D3,
+ DVBT_FUNC4_REG0,
+ DVBT_FUNC4_REG1,
+ DVBT_FUNC4_REG2,
+ DVBT_FUNC4_REG3,
+ DVBT_FUNC4_REG4,
+ DVBT_FUNC4_REG5,
+ DVBT_FUNC4_REG6,
+ DVBT_FUNC4_REG7,
+ DVBT_FUNC4_REG8,
+ DVBT_FUNC4_REG9,
+ DVBT_FUNC4_REG10,
+ DVBT_FUNC5_REG0,
+ DVBT_FUNC5_REG1,
+ DVBT_FUNC5_REG2,
+ DVBT_FUNC5_REG3,
+ DVBT_FUNC5_REG4,
+ DVBT_FUNC5_REG5,
+ DVBT_FUNC5_REG6,
+ DVBT_FUNC5_REG7,
+ DVBT_FUNC5_REG8,
+ DVBT_FUNC5_REG9,
+ DVBT_FUNC5_REG10,
+ DVBT_FUNC5_REG11,
+ DVBT_FUNC5_REG12,
+ DVBT_FUNC5_REG13,
+ DVBT_FUNC5_REG14,
+ DVBT_FUNC5_REG15,
+ DVBT_FUNC5_REG16,
+ DVBT_FUNC5_REG17,
+ DVBT_FUNC5_REG18,
+ DVBT_AD7_SETTING,
+ DVBT_RSSI_R,
+ DVBT_ACI_DET_IND,
+ DVBT_REG_MON,
+ DVBT_REG_MONSEL,
+ DVBT_REG_GPE,
+ DVBT_REG_GPO,
+ DVBT_REG_4MSEL,
+ DVBT_TEST_REG_1,
+ DVBT_TEST_REG_2,
+ DVBT_TEST_REG_3,
+ DVBT_TEST_REG_4,
+ DVBT_REG_BIT_NAME_ITEM_TERMINATOR,
+};
+
+#endif /* RTL2832_PRIV_H */
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
index 2322257c69ae..e2fec9ebf947 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -634,7 +634,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
struct s5h1420_state* state = fe->demodulator_priv;
int frequency_delta;
struct dvb_frontend_tune_settings fesettings;
- uint8_t clock_setting;
dprintk("enter %s\n", __func__);
@@ -679,25 +678,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
else
state->fclk = 44000000;
- /* Clock */
- switch (state->fclk) {
- default:
- case 88000000:
- clock_setting = 80;
- break;
- case 86000000:
- clock_setting = 78;
- break;
- case 80000000:
- clock_setting = 72;
- break;
- case 59000000:
- clock_setting = 51;
- break;
- case 44000000:
- clock_setting = 36;
- break;
- }
dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8);
s5h1420_writereg(state, PLL02, 0x40);
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index 8b0dc74a3298..5d7f8a9b451b 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -1129,7 +1129,6 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
struct stb0899_internal *internal = &state->internal;
u8 lsb, msb;
- u32 i;
*ber = 0;
@@ -1137,14 +1136,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
case SYS_DVBS:
case SYS_DSS:
if (internal->lock) {
- /* average 5 BER values */
- for (i = 0; i < 5; i++) {
- msleep(100);
- lsb = stb0899_read_reg(state, STB0899_ECNT1L);
- msb = stb0899_read_reg(state, STB0899_ECNT1M);
- *ber += MAKEWORD16(msb, lsb);
- }
- *ber /= 5;
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber = MAKEWORD16(msb, lsb);
/* Viterbi Check */
if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
/* Error Rate */
@@ -1157,13 +1151,9 @@ static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
break;
case SYS_DVBS2:
if (internal->lock) {
- /* Average 5 PER values */
- for (i = 0; i < 5; i++) {
- msleep(100);
- lsb = stb0899_read_reg(state, STB0899_ECNT1L);
- msb = stb0899_read_reg(state, STB0899_ECNT1M);
- *ber += MAKEWORD16(msb, lsb);
- }
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber = MAKEWORD16(msb, lsb);
/* ber = ber * 10 ^ 7 */
*ber *= 10000000;
*ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c
index fdd20c7737b5..2a8aaeb1112d 100644
--- a/drivers/media/dvb/frontends/stv0367.c
+++ b/drivers/media/dvb/frontends/stv0367.c
@@ -1584,7 +1584,7 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
struct stv0367ter_state *ter_state = state->ter_state;
int offset = 0, tempo = 0;
u8 u_var;
- u8 /*constell,*/ counter, tps_rcvd[2];
+ u8 /*constell,*/ counter;
s8 step;
s32 timing_offset = 0;
u32 trl_nomrate = 0, InternalFreq = 0, temp = 0;
@@ -1709,9 +1709,6 @@ static int stv0367ter_algo(struct dvb_frontend *fe)
return 0;
ter_state->state = FE_TER_LOCKOK;
- /* update results */
- tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2);
- tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3);
ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE);
ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD);
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index d79e69f65cbb..ea86a5603e57 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -3172,7 +3172,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
u32 reg;
s32 agc1_power, power_iq = 0, i;
- int lock = 0, low_sr = 0, no_signal = 0;
+ int lock = 0, low_sr = 0;
reg = STV090x_READ_DEMOD(state, TSCFGH);
STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */
@@ -3413,7 +3413,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
goto err;
} else {
signal_state = STV090x_NODATA;
- no_signal = stv090x_chk_signal(state);
+ stv090x_chk_signal(state);
}
}
return signal_state;
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
index c21bc92d2811..703c3d05f9f4 100644
--- a/drivers/media/dvb/frontends/tda10071.c
+++ b/drivers/media/dvb/frontends/tda10071.c
@@ -20,10 +20,6 @@
#include "tda10071_priv.h"
-int tda10071_debug;
-module_param_named(debug, tda10071_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
static struct dvb_frontend_ops tda10071_ops;
/* write multiple registers */
@@ -48,7 +44,8 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
if (ret == 1) {
ret = 0;
} else {
- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -79,7 +76,8 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
memcpy(val, buf, len);
ret = 0;
} else {
- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
@@ -170,7 +168,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv,
usleep_range(200, 5000);
}
- dbg("%s: loop=%d", __func__, i);
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
if (i == 0) {
ret = -ETIMEDOUT;
@@ -179,7 +177,7 @@ static int tda10071_cmd_execute(struct tda10071_priv *priv,
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -196,7 +194,8 @@ static int tda10071_set_tone(struct dvb_frontend *fe,
goto error;
}
- dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode);
+ dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__,
+ fe_sec_tone_mode);
switch (fe_sec_tone_mode) {
case SEC_TONE_ON:
@@ -206,24 +205,25 @@ static int tda10071_set_tone(struct dvb_frontend *fe,
tone = 0;
break;
default:
- dbg("%s: invalid fe_sec_tone_mode", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
+ __func__);
ret = -EINVAL;
goto error;
}
- cmd.args[0x00] = CMD_LNB_PCB_CONFIG;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 0x00;
- cmd.args[0x03] = 0x00;
- cmd.args[0x04] = tone;
- cmd.len = 0x05;
+ cmd.args[0] = CMD_LNB_PCB_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = tone;
+ cmd.len = 5;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -240,7 +240,7 @@ static int tda10071_set_voltage(struct dvb_frontend *fe,
goto error;
}
- dbg("%s: voltage=%d", __func__, fe_sec_voltage);
+ dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage);
switch (fe_sec_voltage) {
case SEC_VOLTAGE_13:
@@ -253,22 +253,23 @@ static int tda10071_set_voltage(struct dvb_frontend *fe,
voltage = 0;
break;
default:
- dbg("%s: invalid fe_sec_voltage", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
+ __func__);
ret = -EINVAL;
goto error;
};
- cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = voltage;
- cmd.len = 0x03;
+ cmd.args[0] = CMD_LNB_SET_DC_LEVEL;
+ cmd.args[1] = 0;
+ cmd.args[2] = voltage;
+ cmd.len = 3;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -285,9 +286,10 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
goto error;
}
- dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len);
+ dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__,
+ diseqc_cmd->msg_len);
- if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) {
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
ret = -EINVAL;
goto error;
}
@@ -301,7 +303,7 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
usleep_range(10000, 20000);
}
- dbg("%s: loop=%d", __func__, i);
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
if (i == 0) {
ret = -ETIMEDOUT;
@@ -312,22 +314,22 @@ static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
if (ret)
goto error;
- cmd.args[0x00] = CMD_LNB_SEND_DISEQC;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 0;
- cmd.args[0x03] = 0;
- cmd.args[0x04] = 2;
- cmd.args[0x05] = 0;
- cmd.args[0x06] = diseqc_cmd->msg_len;
- memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len);
- cmd.len = 0x07 + diseqc_cmd->msg_len;
+ cmd.args[0] = CMD_LNB_SEND_DISEQC;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0;
+ cmd.args[3] = 0;
+ cmd.args[4] = 2;
+ cmd.args[5] = 0;
+ cmd.args[6] = diseqc_cmd->msg_len;
+ memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len);
+ cmd.len = 7 + diseqc_cmd->msg_len;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -344,7 +346,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
goto error;
}
- dbg("%s:", __func__);
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
/* wait LNB RX */
for (i = 500, tmp = 0; i && !tmp; i--) {
@@ -355,7 +357,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
usleep_range(10000, 20000);
}
- dbg("%s: loop=%d", __func__, i);
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
if (i == 0) {
ret = -ETIMEDOUT;
@@ -372,9 +374,9 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
reply->msg_len = sizeof(reply->msg); /* truncate API max */
/* read reply */
- cmd.args[0x00] = CMD_LNB_UPDATE_REPLY;
- cmd.args[0x01] = 0;
- cmd.len = 0x02;
+ cmd.args[0] = CMD_LNB_UPDATE_REPLY;
+ cmd.args[1] = 0;
+ cmd.len = 2;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -385,7 +387,7 @@ static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -402,7 +404,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
goto error;
}
- dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd);
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
+ fe_sec_mini_cmd);
switch (fe_sec_mini_cmd) {
case SEC_MINI_A:
@@ -412,7 +415,8 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
burst = 1;
break;
default:
- dbg("%s: invalid fe_sec_mini_cmd", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
+ __func__);
ret = -EINVAL;
goto error;
}
@@ -426,7 +430,7 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
usleep_range(10000, 20000);
}
- dbg("%s: loop=%d", __func__, i);
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
if (i == 0) {
ret = -ETIMEDOUT;
@@ -437,17 +441,17 @@ static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
if (ret)
goto error;
- cmd.args[0x00] = CMD_LNB_SEND_TONEBURST;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = burst;
- cmd.len = 0x03;
+ cmd.args[0] = CMD_LNB_SEND_TONEBURST;
+ cmd.args[1] = 0;
+ cmd.args[2] = burst;
+ cmd.len = 3;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -481,7 +485,7 @@ static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -506,7 +510,7 @@ static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -523,9 +527,9 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
goto error;
}
- cmd.args[0x00] = CMD_GET_AGCACC;
- cmd.args[0x01] = 0;
- cmd.len = 0x02;
+ cmd.args[0] = CMD_GET_AGCACC;
+ cmd.args[1] = 0;
+ cmd.len = 2;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -545,7 +549,7 @@ static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -583,17 +587,18 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
goto error;
if (priv->meas_count[i] == tmp) {
- dbg("%s: meas not ready=%02x", __func__, tmp);
+ dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__,
+ tmp);
*ber = priv->ber;
return 0;
} else {
priv->meas_count[i] = tmp;
}
- cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = i;
- cmd.len = 0x03;
+ cmd.args[0] = CMD_BER_UPDATE_COUNTERS;
+ cmd.args[1] = 0;
+ cmd.args[2] = i;
+ cmd.len = 3;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -612,7 +617,7 @@ static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -632,7 +637,7 @@ static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -644,10 +649,11 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
int ret, i;
u8 mode, rolloff, pilot, inversion, div;
- dbg("%s: delivery_system=%d modulation=%d frequency=%d " \
- "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__,
- c->delivery_system, c->modulation, c->frequency,
- c->symbol_rate, c->inversion, c->pilot, c->rolloff);
+ 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);
priv->delivery_system = SYS_UNDEFINED;
@@ -669,7 +675,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
inversion = 3;
break;
default:
- dbg("%s: invalid inversion", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__);
ret = -EINVAL;
goto error;
}
@@ -692,7 +698,8 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
break;
case ROLLOFF_AUTO:
default:
- dbg("%s: invalid rolloff", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
+ __func__);
ret = -EINVAL;
goto error;
}
@@ -708,13 +715,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
pilot = 2;
break;
default:
- dbg("%s: invalid pilot", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
+ __func__);
ret = -EINVAL;
goto error;
}
break;
default:
- dbg("%s: invalid delivery_system", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
ret = -EINVAL;
goto error;
}
@@ -724,13 +733,15 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
c->modulation == TDA10071_MODCOD[i].modulation &&
c->fec_inner == TDA10071_MODCOD[i].fec) {
mode = TDA10071_MODCOD[i].val;
- dbg("%s: mode found=%02x", __func__, mode);
+ dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n",
+ __func__, mode);
break;
}
}
if (mode == 0xff) {
- dbg("%s: invalid parameter combination", __func__);
+ dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n",
+ __func__);
ret = -EINVAL;
goto error;
}
@@ -748,22 +759,22 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
if (ret)
goto error;
- cmd.args[0x00] = CMD_CHANGE_CHANNEL;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = mode;
- cmd.args[0x03] = (c->frequency >> 16) & 0xff;
- cmd.args[0x04] = (c->frequency >> 8) & 0xff;
- cmd.args[0x05] = (c->frequency >> 0) & 0xff;
- cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff;
- cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff;
- cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
- cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
- cmd.args[0x0a] = rolloff;
- cmd.args[0x0b] = inversion;
- cmd.args[0x0c] = pilot;
- cmd.args[0x0d] = 0x00;
- cmd.args[0x0e] = 0x00;
- cmd.len = 0x0f;
+ cmd.args[0] = CMD_CHANGE_CHANNEL;
+ cmd.args[1] = 0;
+ cmd.args[2] = mode;
+ cmd.args[3] = (c->frequency >> 16) & 0xff;
+ cmd.args[4] = (c->frequency >> 8) & 0xff;
+ cmd.args[5] = (c->frequency >> 0) & 0xff;
+ cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff;
+ cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff;
+ cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
+ cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
+ cmd.args[10] = rolloff;
+ cmd.args[11] = inversion;
+ cmd.args[12] = pilot;
+ cmd.args[13] = 0x00;
+ cmd.args[14] = 0x00;
+ cmd.len = 15;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -772,7 +783,7 @@ static int tda10071_set_frontend(struct dvb_frontend *fe)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -829,7 +840,7 @@ static int tda10071_get_frontend(struct dvb_frontend *fe)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -915,10 +926,10 @@ static int tda10071_init(struct dvb_frontend *fe)
goto error;
}
- cmd.args[0x00] = CMD_SET_SLEEP_MODE;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 0;
- cmd.len = 0x03;
+ cmd.args[0] = CMD_SET_SLEEP_MODE;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0;
+ cmd.len = 3;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -929,10 +940,11 @@ static int tda10071_init(struct dvb_frontend *fe)
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
if (ret) {
- err("did not find the firmware file. (%s) "
- "Please see linux/Documentation/dvb/ for more" \
- " details on firmware-problems. (%d)",
- fw_file, ret);
+ dev_err(&priv->i2c->dev, "%s: did not find the " \
+ "firmware file. (%s) Please see " \
+ "linux/Documentation/dvb/ for more " \
+ "details on firmware-problems. (%d)\n",
+ KBUILD_MODNAME, fw_file, ret);
goto error;
}
@@ -961,10 +973,11 @@ static int tda10071_init(struct dvb_frontend *fe)
if (ret)
goto error_release_firmware;
- info("found a '%s' in cold state, will try to load a firmware",
- tda10071_ops.info.name);
-
- info("downloading firmware from file '%s'", fw_file);
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \
+ "will try to load a firmware\n", KBUILD_MODNAME,
+ tda10071_ops.info.name);
+ dev_info(&priv->i2c->dev, "%s: downloading firmware from " \
+ "file '%s'\n", KBUILD_MODNAME, fw_file);
/* do not download last byte */
fw_size = fw->size - 1;
@@ -978,7 +991,9 @@ static int tda10071_init(struct dvb_frontend *fe)
ret = tda10071_wr_regs(priv, 0xfa,
(u8 *) &fw->data[fw_size - remaining], len);
if (ret) {
- err("firmware download failed=%d", ret);
+ dev_err(&priv->i2c->dev, "%s: firmware " \
+ "download failed=%d\n",
+ KBUILD_MODNAME, ret);
if (ret)
goto error_release_firmware;
}
@@ -1002,15 +1017,16 @@ static int tda10071_init(struct dvb_frontend *fe)
goto error;
if (tmp) {
- info("firmware did not run");
+ dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
ret = -EFAULT;
goto error;
} else {
priv->warm = 1;
}
- cmd.args[0x00] = CMD_GET_FW_VERSION;
- cmd.len = 0x01;
+ cmd.args[0] = CMD_GET_FW_VERSION;
+ cmd.len = 1;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -1019,54 +1035,55 @@ static int tda10071_init(struct dvb_frontend *fe)
if (ret)
goto error;
- info("firmware version %d.%d.%d.%d",
- buf[0], buf[1], buf[2], buf[3]);
- info("found a '%s' in warm state.", tda10071_ops.info.name);
+ dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
+ KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
+ KBUILD_MODNAME, tda10071_ops.info.name);
ret = tda10071_rd_regs(priv, 0x81, buf, 2);
if (ret)
goto error;
- cmd.args[0x00] = CMD_DEMOD_INIT;
- cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
- cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
- cmd.args[0x03] = buf[0];
- cmd.args[0x04] = buf[1];
- cmd.args[0x05] = priv->cfg.pll_multiplier;
- cmd.args[0x06] = priv->cfg.spec_inv;
- cmd.args[0x07] = 0x00;
- cmd.len = 0x08;
+ cmd.args[0] = CMD_DEMOD_INIT;
+ cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
+ cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
+ cmd.args[3] = buf[0];
+ cmd.args[4] = buf[1];
+ cmd.args[5] = priv->cfg.pll_multiplier;
+ cmd.args[6] = priv->cfg.spec_inv;
+ cmd.args[7] = 0x00;
+ cmd.len = 8;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
- cmd.args[0x00] = CMD_TUNER_INIT;
- cmd.args[0x01] = 0x00;
- cmd.args[0x02] = 0x00;
- cmd.args[0x03] = 0x00;
- cmd.args[0x04] = 0x00;
- cmd.args[0x05] = 0x14;
- cmd.args[0x06] = 0x00;
- cmd.args[0x07] = 0x03;
- cmd.args[0x08] = 0x02;
- cmd.args[0x09] = 0x02;
- cmd.args[0x0a] = 0x00;
- cmd.args[0x0b] = 0x00;
- cmd.args[0x0c] = 0x00;
- cmd.args[0x0d] = 0x00;
- cmd.args[0x0e] = 0x00;
- cmd.len = 0x0f;
+ cmd.args[0] = CMD_TUNER_INIT;
+ cmd.args[1] = 0x00;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x00;
+ cmd.args[5] = 0x14;
+ cmd.args[6] = 0x00;
+ cmd.args[7] = 0x03;
+ cmd.args[8] = 0x02;
+ cmd.args[9] = 0x02;
+ cmd.args[10] = 0x00;
+ cmd.args[11] = 0x00;
+ cmd.args[12] = 0x00;
+ cmd.args[13] = 0x00;
+ cmd.args[14] = 0x00;
+ cmd.len = 15;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
- cmd.args[0x00] = CMD_MPEG_CONFIG;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = priv->cfg.ts_mode;
- cmd.args[0x03] = 0x00;
- cmd.args[0x04] = 0x04;
- cmd.args[0x05] = 0x00;
- cmd.len = 0x06;
+ cmd.args[0] = CMD_MPEG_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = priv->cfg.ts_mode;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x04;
+ cmd.args[5] = 0x00;
+ cmd.len = 6;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -1075,27 +1092,27 @@ static int tda10071_init(struct dvb_frontend *fe)
if (ret)
goto error;
- cmd.args[0x00] = CMD_LNB_CONFIG;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 150;
- cmd.args[0x03] = 3;
- cmd.args[0x04] = 22;
- cmd.args[0x05] = 1;
- cmd.args[0x06] = 1;
- cmd.args[0x07] = 30;
- cmd.args[0x08] = 30;
- cmd.args[0x09] = 30;
- cmd.args[0x0a] = 30;
- cmd.len = 0x0b;
+ cmd.args[0] = CMD_LNB_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = 150;
+ cmd.args[3] = 3;
+ cmd.args[4] = 22;
+ cmd.args[5] = 1;
+ cmd.args[6] = 1;
+ cmd.args[7] = 30;
+ cmd.args[8] = 30;
+ cmd.args[9] = 30;
+ cmd.args[10] = 30;
+ cmd.len = 11;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
- cmd.args[0x00] = CMD_BER_CONTROL;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 14;
- cmd.args[0x03] = 14;
- cmd.len = 0x04;
+ cmd.args[0] = CMD_BER_CONTROL;
+ cmd.args[1] = 0;
+ cmd.args[2] = 14;
+ cmd.args[3] = 14;
+ cmd.len = 4;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -1105,7 +1122,7 @@ static int tda10071_init(struct dvb_frontend *fe)
error_release_firmware:
release_firmware(fw);
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -1132,10 +1149,10 @@ static int tda10071_sleep(struct dvb_frontend *fe)
goto error;
}
- cmd.args[0x00] = CMD_SET_SLEEP_MODE;
- cmd.args[0x01] = 0;
- cmd.args[0x02] = 1;
- cmd.len = 0x03;
+ cmd.args[0] = CMD_SET_SLEEP_MODE;
+ cmd.args[1] = 0;
+ cmd.args[2] = 1;
+ cmd.len = 3;
ret = tda10071_cmd_execute(priv, &cmd);
if (ret)
goto error;
@@ -1149,7 +1166,7 @@ static int tda10071_sleep(struct dvb_frontend *fe)
return ret;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -1208,7 +1225,7 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
return &priv->fe;
error:
- dbg("%s: failed=%d", __func__, ret);
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
kfree(priv);
return NULL;
}
diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h
index 93c5e6317f07..0fa85cfa70c2 100644
--- a/drivers/media/dvb/frontends/tda10071_priv.h
+++ b/drivers/media/dvb/frontends/tda10071_priv.h
@@ -25,19 +25,6 @@
#include "tda10071.h"
#include <linux/firmware.h>
-#define LOG_PREFIX "tda10071"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (tda10071_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
struct tda10071_priv {
struct i2c_adapter *i2c;
struct dvb_frontend fe;
@@ -112,7 +99,7 @@ struct tda10071_reg_val_mask {
#define CMD_BER_UPDATE_COUNTERS 0x3f
/* firmare command struct */
-#define TDA10071_ARGLEN 0x1e
+#define TDA10071_ARGLEN 30
struct tda10071_cmd {
u8 args[TDA10071_ARGLEN];
u8 len;
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 7539a5d71029..72ee8de02260 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -217,6 +217,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
memset(&config, 0, sizeof(config));
config.microcode_name = "drxk_a3.mc";
+ config.qam_demod_parameter_count = 4;
config.adr = 0x29 + (chan->number ^ 2);
chan->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 7331e8450d1a..9cc55546cc30 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -276,16 +276,13 @@ static void smscore_notify_clients(struct smscore_device_t *coredev)
static int smscore_notify_callbacks(struct smscore_device_t *coredev,
struct device *device, int arrival)
{
- struct list_head *next, *first;
+ struct smscore_device_notifyee_t *elem;
int rc = 0;
/* note: must be called under g_deviceslock */
- first = &g_smscore_notifyees;
-
- for (next = first->next; next != first; next = next->next) {
- rc = ((struct smscore_device_notifyee_t *) next)->
- hotplug(coredev, device, arrival);
+ list_for_each_entry(elem, &g_smscore_notifyees, entry) {
+ rc = elem->hotplug(coredev, device, arrival);
if (rc < 0)
break;
}
@@ -940,29 +937,25 @@ static struct
smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
int data_type, int id)
{
- struct smscore_client_t *client = NULL;
- struct list_head *next, *first;
+ struct list_head *first;
+ struct smscore_client_t *client;
unsigned long flags;
- struct list_head *firstid, *nextid;
-
+ struct list_head *firstid;
+ struct smscore_idlist_t *client_id;
spin_lock_irqsave(&coredev->clientslock, flags);
first = &coredev->clients;
- for (next = first->next;
- (next != first) && !client;
- next = next->next) {
- firstid = &((struct smscore_client_t *)next)->idlist;
- for (nextid = firstid->next;
- nextid != firstid;
- nextid = nextid->next) {
- if ((((struct smscore_idlist_t *)nextid)->id == id) &&
- (((struct smscore_idlist_t *)nextid)->data_type == data_type ||
- (((struct smscore_idlist_t *)nextid)->data_type == 0))) {
- client = (struct smscore_client_t *) next;
- break;
- }
+ list_for_each_entry(client, first, entry) {
+ firstid = &client->idlist;
+ list_for_each_entry(client_id, firstid, entry) {
+ if ((client_id->id == id) &&
+ (client_id->data_type == data_type ||
+ (client_id->data_type == 0)))
+ goto found;
}
}
+ client = NULL;
+found:
spin_unlock_irqrestore(&coredev->clientslock, flags);
return client;
}
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index c257da13d766..8090b87b3066 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -5,6 +5,7 @@
menuconfig RADIO_ADAPTERS
bool "Radio Adapters"
depends on VIDEO_V4L2
+ depends on MEDIA_RADIO_SUPPORT
default y
---help---
Say Y here to enable selecting AM/FM radio adapters.
@@ -56,6 +57,39 @@ config RADIO_MAXIRADIO
To compile this driver as a module, choose M here: the
module will be called radio-maxiradio.
+config RADIO_SHARK
+ tristate "Griffin radioSHARK USB radio receiver"
+ depends on USB && SND
+ ---help---
+ Choose Y here if you have this radio receiver.
+
+ There are 2 versions of this device, this driver is for version 1,
+ which is white.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-shark.
+
+config RADIO_SHARK2
+ tristate "Griffin radioSHARK2 USB radio receiver"
+ depends on USB
+ ---help---
+ Choose Y here if you have this radio receiver.
+
+ There are 2 versions of this device, this driver is for version 2,
+ which is black.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-shark2.
config I2C_SI4713
tristate "I2C driver for Silicon Labs Si4713 device"
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index ca8c7d134b95..c03ce4fe74e9 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
+obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
+obj-$(CONFIG_RADIO_SHARK2) += shark2.o
obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
@@ -29,4 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o
obj-$(CONFIG_RADIO_WL128X) += wl128x/
+shark2-objs := radio-shark2.o radio-tea5777.o
+
ccflags-y += -Isound
diff --git a/drivers/media/radio/lm7000.h b/drivers/media/radio/lm7000.h
new file mode 100644
index 000000000000..139cd6b68824
--- /dev/null
+++ b/drivers/media/radio/lm7000.h
@@ -0,0 +1,43 @@
+#ifndef __LM7000_H
+#define __LM7000_H
+
+/* Sanyo LM7000 tuner chip control
+ *
+ * Copyright 2012 Ondrej Zary <linux@rainbow-software.org>
+ * based on radio-aimslab.c by M. Kirkwood
+ * and radio-sf16fmi.c by M. Kirkwood and Petr Vandrovec
+ */
+
+#define LM7000_DATA (1 << 0)
+#define LM7000_CLK (1 << 1)
+#define LM7000_CE (1 << 2)
+
+#define LM7000_FM_100 (0 << 20)
+#define LM7000_FM_50 (1 << 20)
+#define LM7000_FM_25 (2 << 20)
+#define LM7000_BIT_FM (1 << 23)
+
+static inline void lm7000_set_freq(u32 freq, void *handle,
+ void (*set_pins)(void *handle, u8 pins))
+{
+ int i;
+ u8 data;
+ u32 val;
+
+ freq += 171200; /* Add 10.7 MHz IF */
+ freq /= 400; /* Convert to 25 kHz units */
+ val = freq | LM7000_FM_25 | LM7000_BIT_FM;
+ /* write the 24-bit register, starting with LSB */
+ for (i = 0; i < 24; i++) {
+ data = val & (1 << i) ? LM7000_DATA : 0;
+ set_pins(handle, data | LM7000_CE);
+ udelay(2);
+ set_pins(handle, data | LM7000_CE | LM7000_CLK);
+ udelay(2);
+ set_pins(handle, data | LM7000_CE);
+ udelay(2);
+ }
+ set_pins(handle, 0);
+}
+
+#endif /* __LM7000_H */
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 98e0c8c20312..12c70e876f58 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -37,6 +37,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include "radio-isa.h"
+#include "lm7000.h"
MODULE_AUTHOR("M. Kirkwood");
MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
@@ -72,55 +73,38 @@ static struct radio_isa_card *rtrack_alloc(void)
return rt ? &rt->isa : NULL;
}
-/* The 128+64 on these outb's is to keep the volume stable while tuning.
- * Without them, the volume _will_ creep up with each frequency change
- * and bit 4 (+16) is to keep the signal strength meter enabled.
- */
+#define AIMS_BIT_TUN_CE (1 << 0)
+#define AIMS_BIT_TUN_CLK (1 << 1)
+#define AIMS_BIT_TUN_DATA (1 << 2)
+#define AIMS_BIT_VOL_CE (1 << 3)
+#define AIMS_BIT_TUN_STRQ (1 << 4)
+/* bit 5 is not connected */
+#define AIMS_BIT_VOL_UP (1 << 6) /* active low */
+#define AIMS_BIT_VOL_DN (1 << 7) /* active low */
-static void send_0_byte(struct radio_isa_card *isa, int on)
+void rtrack_set_pins(void *handle, u8 pins)
{
- outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */
- outb_p(128+64+16+on+2+1, isa->io); /* clock */
- msleep(1);
-}
+ struct radio_isa_card *isa = handle;
+ struct rtrack *rt = container_of(isa, struct rtrack, isa);
+ u8 bits = AIMS_BIT_VOL_DN | AIMS_BIT_VOL_UP | AIMS_BIT_TUN_STRQ;
-static void send_1_byte(struct radio_isa_card *isa, int on)
-{
- outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */
- outb_p(128+64+16+on+4+2+1, isa->io); /* clock */
- msleep(1);
+ if (!v4l2_ctrl_g_ctrl(rt->isa.mute))
+ bits |= AIMS_BIT_VOL_CE;
+
+ if (pins & LM7000_DATA)
+ bits |= AIMS_BIT_TUN_DATA;
+ if (pins & LM7000_CLK)
+ bits |= AIMS_BIT_TUN_CLK;
+ if (pins & LM7000_CE)
+ bits |= AIMS_BIT_TUN_CE;
+
+ outb_p(bits, rt->isa.io);
}
static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
- int i;
-
- freq += 171200; /* Add 10.7 MHz IF */
- freq /= 800; /* Convert to 50 kHz units */
-
- send_0_byte(isa, on); /* 0: LSB of frequency */
-
- for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
- if (freq & (1 << i))
- send_1_byte(isa, on);
- else
- send_0_byte(isa, on);
-
- send_0_byte(isa, on); /* 14: test bit - always 0 */
- send_0_byte(isa, on); /* 15: test bit - always 0 */
-
- send_0_byte(isa, on); /* 16: band data 0 - always 0 */
- send_0_byte(isa, on); /* 17: band data 1 - always 0 */
- send_0_byte(isa, on); /* 18: band data 2 - always 0 */
- send_0_byte(isa, on); /* 19: time base - always 0 */
-
- send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */
- send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */
- send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */
- send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */
+ lm7000_set_freq(freq, isa, rtrack_set_pins);
- outb(0xd0 + on, isa->io); /* volume steady + sigstr */
return 0;
}
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 16a089fad909..697a421c9940 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -41,6 +41,9 @@
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
@@ -61,14 +64,15 @@ module_param(radio_nr, int, 0);
struct cadet {
struct v4l2_device v4l2_dev;
struct video_device vdev;
+ struct v4l2_ctrl_handler ctrl_handler;
int io;
- int users;
- int curtuner;
+ bool is_fm_band;
+ u32 curfreq;
int tunestat;
int sigstrength;
wait_queue_head_t read_queue;
struct timer_list readtimer;
- __u8 rdsin, rdsout, rdsstat;
+ u8 rdsin, rdsout, rdsstat;
unsigned char rdsbuf[RDS_BUFFER];
struct mutex lock;
int reading;
@@ -81,9 +85,9 @@ static struct cadet cadet_card;
* The V4L API spec does not define any particular unit for the signal
* strength value. These values are in microvolts of RF at the tuner's input.
*/
-static __u16 sigtable[2][4] = {
- { 5, 10, 30, 150 },
- { 28, 40, 63, 1000 }
+static u16 sigtable[2][4] = {
+ { 1835, 2621, 4128, 65535 },
+ { 2185, 4369, 13107, 65535 },
};
@@ -91,14 +95,12 @@ static int cadet_getstereo(struct cadet *dev)
{
int ret = V4L2_TUNER_SUB_MONO;
- if (dev->curtuner != 0) /* Only FM has stereo capability! */
+ if (!dev->is_fm_band) /* Only FM has stereo capability! */
return V4L2_TUNER_SUB_MONO;
- mutex_lock(&dev->lock);
outb(7, dev->io); /* Select tuner control */
if ((inb(dev->io + 1) & 0x40) == 0)
ret = V4L2_TUNER_SUB_STEREO;
- mutex_unlock(&dev->lock);
return ret;
}
@@ -111,8 +113,6 @@ static unsigned cadet_gettune(struct cadet *dev)
* Prepare for read
*/
- mutex_lock(&dev->lock);
-
outb(7, dev->io); /* Select tuner control */
curvol = inb(dev->io + 1); /* Save current volume/mute setting */
outb(0x00, dev->io + 1); /* Ensure WRITE-ENABLE is LOW */
@@ -134,8 +134,6 @@ static unsigned cadet_gettune(struct cadet *dev)
* Restore volume/mute setting
*/
outb(curvol, dev->io + 1);
- mutex_unlock(&dev->lock);
-
return fifo;
}
@@ -152,20 +150,18 @@ static unsigned cadet_getfreq(struct cadet *dev)
/*
* Convert to actual frequency
*/
- if (dev->curtuner == 0) { /* FM */
- test = 12500;
- for (i = 0; i < 14; i++) {
- if ((fifo & 0x01) != 0)
- freq += test;
- test = test << 1;
- fifo = fifo >> 1;
- }
- freq -= 10700000; /* IF frequency is 10.7 MHz */
- freq = (freq * 16) / 1000000; /* Make it 1/16 MHz */
+ if (!dev->is_fm_band) /* AM */
+ return ((fifo & 0x7fff) - 450) * 16;
+
+ test = 12500;
+ for (i = 0; i < 14; i++) {
+ if ((fifo & 0x01) != 0)
+ freq += test;
+ test = test << 1;
+ fifo = fifo >> 1;
}
- if (dev->curtuner == 1) /* AM */
- freq = ((fifo & 0x7fff) - 2010) * 16;
-
+ freq -= 10700000; /* IF frequency is 10.7 MHz */
+ freq = (freq * 16) / 1000; /* Make it 1/16 kHz */
return freq;
}
@@ -174,8 +170,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
int i;
unsigned test;
- mutex_lock(&dev->lock);
-
outb(7, dev->io); /* Select tuner control */
/*
* Write the shift register
@@ -194,7 +188,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
test = 0x1c | ((fifo >> 23) & 0x02);
outb(test, dev->io + 1);
}
- mutex_unlock(&dev->lock);
}
static void cadet_setfreq(struct cadet *dev, unsigned freq)
@@ -203,13 +196,14 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
int i, j, test;
int curvol;
+ dev->curfreq = freq;
/*
* Formulate a fifo command
*/
fifo = 0;
- if (dev->curtuner == 0) { /* FM */
+ if (dev->is_fm_band) { /* FM */
test = 102400;
- freq = (freq * 1000) / 16; /* Make it kHz */
+ freq = freq / 16; /* Make it kHz */
freq += 10700; /* IF is 10700 kHz */
for (i = 0; i < 14; i++) {
fifo = fifo << 1;
@@ -219,20 +213,17 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
}
test = test >> 1;
}
- }
- if (dev->curtuner == 1) { /* AM */
- fifo = (freq / 16) + 2010; /* Make it kHz */
- fifo |= 0x100000; /* Select AM Band */
+ } else { /* AM */
+ fifo = (freq / 16) + 450; /* Make it kHz */
+ fifo |= 0x100000; /* Select AM Band */
}
/*
* Save current volume/mute setting
*/
- mutex_lock(&dev->lock);
outb(7, dev->io); /* Select tuner control */
curvol = inb(dev->io + 1);
- mutex_unlock(&dev->lock);
/*
* Tune the card
@@ -240,49 +231,24 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
for (j = 3; j > -1; j--) {
cadet_settune(dev, fifo | (j << 16));
- mutex_lock(&dev->lock);
outb(7, dev->io); /* Select tuner control */
outb(curvol, dev->io + 1);
- mutex_unlock(&dev->lock);
msleep(100);
cadet_gettune(dev);
if ((dev->tunestat & 0x40) == 0) { /* Tuned */
- dev->sigstrength = sigtable[dev->curtuner][j];
- return;
+ dev->sigstrength = sigtable[dev->is_fm_band][j];
+ goto reset_rds;
}
}
dev->sigstrength = 0;
+reset_rds:
+ outb(3, dev->io);
+ outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
}
-static int cadet_getvol(struct cadet *dev)
-{
- int ret = 0;
-
- mutex_lock(&dev->lock);
-
- outb(7, dev->io); /* Select tuner control */
- if ((inb(dev->io + 1) & 0x20) != 0)
- ret = 0xffff;
-
- mutex_unlock(&dev->lock);
- return ret;
-}
-
-
-static void cadet_setvol(struct cadet *dev, int vol)
-{
- mutex_lock(&dev->lock);
- outb(7, dev->io); /* Select tuner control */
- if (vol > 0)
- outb(0x20, dev->io + 1);
- else
- outb(0x00, dev->io + 1);
- mutex_unlock(&dev->lock);
-}
-
static void cadet_handler(unsigned long data)
{
struct cadet *dev = (void *)data;
@@ -295,7 +261,7 @@ static void cadet_handler(unsigned long data)
outb(0x80, dev->io); /* Select RDS fifo */
while ((inb(dev->io) & 0x80) != 0) {
dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
- if (dev->rdsin == dev->rdsout)
+ if (dev->rdsin + 1 == dev->rdsout)
printk(KERN_WARNING "cadet: RDS buffer overflow\n");
else
dev->rdsin++;
@@ -314,11 +280,21 @@ static void cadet_handler(unsigned long data)
*/
init_timer(&dev->readtimer);
dev->readtimer.function = cadet_handler;
- dev->readtimer.data = (unsigned long)0;
+ dev->readtimer.data = data;
dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
add_timer(&dev->readtimer);
}
+static void cadet_start_rds(struct cadet *dev)
+{
+ dev->rdsstat = 1;
+ outb(0x80, dev->io); /* Select RDS fifo */
+ init_timer(&dev->readtimer);
+ dev->readtimer.function = cadet_handler;
+ dev->readtimer.data = (unsigned long)dev;
+ dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
+ add_timer(&dev->readtimer);
+}
static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
@@ -327,28 +303,24 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
int i = 0;
mutex_lock(&dev->lock);
- if (dev->rdsstat == 0) {
- dev->rdsstat = 1;
- outb(0x80, dev->io); /* Select RDS fifo */
- init_timer(&dev->readtimer);
- dev->readtimer.function = cadet_handler;
- dev->readtimer.data = (unsigned long)dev;
- dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
- add_timer(&dev->readtimer);
- }
+ if (dev->rdsstat == 0)
+ cadet_start_rds(dev);
if (dev->rdsin == dev->rdsout) {
+ if (file->f_flags & O_NONBLOCK) {
+ i = -EWOULDBLOCK;
+ goto unlock;
+ }
mutex_unlock(&dev->lock);
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
interruptible_sleep_on(&dev->read_queue);
mutex_lock(&dev->lock);
}
while (i < count && dev->rdsin != dev->rdsout)
readbuf[i++] = dev->rdsbuf[dev->rdsout++];
- mutex_unlock(&dev->lock);
- if (copy_to_user(data, readbuf, i))
- return -EFAULT;
+ if (i && copy_to_user(data, readbuf, i))
+ i = -EFAULT;
+unlock:
+ mutex_unlock(&dev->lock);
return i;
}
@@ -359,48 +331,58 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
strlcpy(v->card, "ADS Cadet", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .index = 0,
+ .type = V4L2_TUNER_RADIO,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 8320, /* 520 kHz */
+ .rangehigh = 26400, /* 1650 kHz */
+ .modulation = V4L2_BAND_MODULATION_AM,
+ }, {
+ .index = 1,
+ .type = V4L2_TUNER_RADIO,
+ .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
+ V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
+ V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 1400000, /* 87.5 MHz */
+ .rangehigh = 1728000, /* 108.0 MHz */
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+};
+
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct cadet *dev = video_drvdata(file);
+ if (v->index)
+ return -EINVAL;
v->type = V4L2_TUNER_RADIO;
- switch (v->index) {
- case 0:
- strlcpy(v->name, "FM", sizeof(v->name));
- v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
- V4L2_TUNER_CAP_RDS_BLOCK_IO;
- v->rangelow = 1400; /* 87.5 MHz */
- v->rangehigh = 1728; /* 108.0 MHz */
+ strlcpy(v->name, "Radio", sizeof(v->name));
+ v->capability = bands[0].capability | bands[1].capability;
+ v->rangelow = bands[0].rangelow; /* 520 kHz (start of AM band) */
+ v->rangehigh = bands[1].rangehigh; /* 108.0 MHz (end of FM band) */
+ if (dev->is_fm_band) {
v->rxsubchans = cadet_getstereo(dev);
- switch (v->rxsubchans) {
- case V4L2_TUNER_SUB_MONO:
- v->audmode = V4L2_TUNER_MODE_MONO;
- break;
- case V4L2_TUNER_SUB_STEREO:
- v->audmode = V4L2_TUNER_MODE_STEREO;
- break;
- default:
- break;
- }
- v->rxsubchans |= V4L2_TUNER_SUB_RDS;
- break;
- case 1:
- strlcpy(v->name, "AM", sizeof(v->name));
- v->capability = V4L2_TUNER_CAP_LOW;
+ outb(3, dev->io);
+ outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
+ mdelay(100);
+ outb(3, dev->io);
+ if (inb(dev->io + 1) & 0x80)
+ v->rxsubchans |= V4L2_TUNER_SUB_RDS;
+ } else {
v->rangelow = 8320; /* 520 kHz */
v->rangehigh = 26400; /* 1650 kHz */
v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->audmode = V4L2_TUNER_MODE_MONO;
- break;
- default:
- return -EINVAL;
}
+ v->audmode = V4L2_TUNER_MODE_STEREO;
v->signal = dev->sigstrength; /* We might need to modify scaling of this */
return 0;
}
@@ -408,11 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct cadet *dev = video_drvdata(file);
+ return v->index ? -EINVAL : 0;
+}
- if (v->index != 0 && v->index != 1)
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ if (band->tuner)
+ return -EINVAL;
+ if (band->index >= ARRAY_SIZE(bands))
return -EINVAL;
- dev->curtuner = v->index;
+ *band = bands[band->index];
return 0;
}
@@ -421,9 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct cadet *dev = video_drvdata(file);
- f->tuner = dev->curtuner;
+ if (f->tuner)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
- f->frequency = cadet_getfreq(dev);
+ f->frequency = dev->curfreq;
return 0;
}
@@ -433,103 +422,46 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct cadet *dev = video_drvdata(file);
- if (f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
- return -EINVAL;
- if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
+ if (f->tuner)
return -EINVAL;
+ dev->is_fm_band =
+ f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
+ clamp(f->frequency, bands[dev->is_fm_band].rangelow,
+ bands[dev->is_fm_band].rangehigh);
cadet_setfreq(dev, f->frequency);
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct cadet *dev = video_drvdata(file);
+ struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
- ctrl->value = (cadet_getvol(dev) == 0);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = cadet_getvol(dev);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct cadet *dev = video_drvdata(file);
-
- switch (ctrl->id){
- case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
- if (ctrl->value)
- cadet_setvol(dev, 0);
+ case V4L2_CID_AUDIO_MUTE:
+ outb(7, dev->io); /* Select tuner control */
+ if (ctrl->val)
+ outb(0x00, dev->io + 1);
else
- cadet_setvol(dev, 0xffff);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- cadet_setvol(dev, ctrl->value);
- break;
- default:
- return -EINVAL;
+ outb(0x20, dev->io + 1);
+ return 0;
}
- return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
+ return -EINVAL;
}
static int cadet_open(struct file *file)
{
struct cadet *dev = video_drvdata(file);
+ int err;
mutex_lock(&dev->lock);
- dev->users++;
- if (1 == dev->users)
+ err = v4l2_fh_open(file);
+ if (err)
+ goto fail;
+ if (v4l2_fh_is_singular_file(file))
init_waitqueue_head(&dev->read_queue);
+fail:
mutex_unlock(&dev->lock);
- return 0;
+ return err;
}
static int cadet_release(struct file *file)
@@ -537,11 +469,11 @@ static int cadet_release(struct file *file)
struct cadet *dev = video_drvdata(file);
mutex_lock(&dev->lock);
- dev->users--;
- if (0 == dev->users) {
+ if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
del_timer_sync(&dev->readtimer);
dev->rdsstat = 0;
}
+ v4l2_fh_release(file);
mutex_unlock(&dev->lock);
return 0;
}
@@ -549,11 +481,19 @@ static int cadet_release(struct file *file)
static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
{
struct cadet *dev = video_drvdata(file);
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned int res = v4l2_ctrl_poll(file, wait);
poll_wait(file, &dev->read_queue, wait);
+ if (dev->rdsstat == 0 && (req_events & (POLLIN | POLLRDNORM))) {
+ mutex_lock(&dev->lock);
+ if (dev->rdsstat == 0)
+ cadet_start_rds(dev);
+ mutex_unlock(&dev->lock);
+ }
if (dev->rdsin != dev->rdsout)
- return POLLIN | POLLRDNORM;
- return 0;
+ res |= POLLIN | POLLRDNORM;
+ return res;
}
@@ -572,13 +512,14 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+ .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
+ .s_ctrl = cadet_s_ctrl,
};
#ifdef CONFIG_PNP
@@ -628,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
for (i = 0; i < 8; i++) {
dev->io = iovals[i];
if (request_region(dev->io, 2, "cadet-probe")) {
- cadet_setfreq(dev, 1410);
- if (cadet_getfreq(dev) == 1410) {
+ cadet_setfreq(dev, bands[1].rangelow);
+ if (cadet_getfreq(dev) == bands[1].rangelow) {
release_region(dev->io, 2);
return;
}
@@ -648,7 +589,8 @@ static int __init cadet_init(void)
{
struct cadet *dev = &cadet_card;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- int res;
+ struct v4l2_ctrl_handler *hdl;
+ int res = -ENODEV;
strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
mutex_init(&dev->lock);
@@ -680,23 +622,40 @@ static int __init cadet_init(void)
goto fail;
}
+ hdl = &dev->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 2);
+ v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ v4l2_dev->ctrl_handler = hdl;
+ if (hdl->error) {
+ res = hdl->error;
+ v4l2_err(v4l2_dev, "Could not register controls\n");
+ goto err_hdl;
+ }
+
+ dev->is_fm_band = true;
+ dev->curfreq = bands[dev->is_fm_band].rangelow;
+ cadet_setfreq(dev, dev->curfreq);
strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
dev->vdev.v4l2_dev = v4l2_dev;
dev->vdev.fops = &cadet_fops;
dev->vdev.ioctl_ops = &cadet_ioctl_ops;
dev->vdev.release = video_device_release_empty;
+ dev->vdev.lock = &dev->lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
video_set_drvdata(&dev->vdev, dev);
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(dev->io, 2);
- goto fail;
- }
+ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ goto err_hdl;
v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
return 0;
+err_hdl:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(v4l2_dev);
+ release_region(dev->io, 2);
fail:
pnp_unregister_driver(&cadet_pnp_driver);
- return -ENODEV;
+ return res;
}
static void __exit cadet_exit(void)
@@ -704,7 +663,10 @@ static void __exit cadet_exit(void)
struct cadet *dev = &cadet_card;
video_unregister_device(&dev->vdev);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
v4l2_device_unregister(&dev->v4l2_dev);
+ outb(7, dev->io); /* Mute */
+ outb(0x00, dev->io + 1);
release_region(dev->io, 2);
pnp_unregister_driver(&cadet_pnp_driver);
}
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 94cb6bc690f5..3182b26d6efa 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -295,7 +295,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
v->type = V4L2_TUNER_RADIO;
v->rangelow = FREQ_MIN * FREQ_MUL;
v->rangehigh = FREQ_MAX * FREQ_MUL;
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_HWSEEK_WRAP;
v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
v->audmode = radio->stereo ?
V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
@@ -372,7 +373,7 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *priv,
timeout = jiffies + msecs_to_jiffies(30000);
for (;;) {
if (time_after(jiffies, timeout)) {
- retval = -EAGAIN;
+ retval = -ENODATA;
break;
}
if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index a81d723b8c77..8185d5fbfa89 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -27,6 +27,7 @@
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "lm7000.h"
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio.");
@@ -54,31 +55,33 @@ static struct fmi fmi_card;
static struct pnp_dev *dev;
bool pnp_attached;
-/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
-/* It is only useful to give freq in interval of 800 (=0.05Mhz),
- * other bits will be truncated, e.g 92.7400016 -> 92.7, but
- * 92.7400017 -> 92.75
- */
-#define RSF16_ENCODE(x) ((x) / 800 + 214)
#define RSF16_MINFREQ (87 * 16000)
#define RSF16_MAXFREQ (108 * 16000)
-static void outbits(int bits, unsigned int data, int io)
+#define FMI_BIT_TUN_CE (1 << 0)
+#define FMI_BIT_TUN_CLK (1 << 1)
+#define FMI_BIT_TUN_DATA (1 << 2)
+#define FMI_BIT_VOL_SW (1 << 3)
+#define FMI_BIT_TUN_STRQ (1 << 4)
+
+void fmi_set_pins(void *handle, u8 pins)
{
- while (bits--) {
- if (data & 1) {
- outb(5, io);
- udelay(6);
- outb(7, io);
- udelay(6);
- } else {
- outb(1, io);
- udelay(6);
- outb(3, io);
- udelay(6);
- }
- data >>= 1;
- }
+ struct fmi *fmi = handle;
+ u8 bits = FMI_BIT_TUN_STRQ;
+
+ if (!fmi->mute)
+ bits |= FMI_BIT_VOL_SW;
+
+ if (pins & LM7000_DATA)
+ bits |= FMI_BIT_TUN_DATA;
+ if (pins & LM7000_CLK)
+ bits |= FMI_BIT_TUN_CLK;
+ if (pins & LM7000_CE)
+ bits |= FMI_BIT_TUN_CE;
+
+ mutex_lock(&fmi->lock);
+ outb_p(bits, fmi->io);
+ mutex_unlock(&fmi->lock);
}
static inline void fmi_mute(struct fmi *fmi)
@@ -95,20 +98,6 @@ static inline void fmi_unmute(struct fmi *fmi)
mutex_unlock(&fmi->lock);
}
-static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
-{
- mutex_lock(&fmi->lock);
- fmi->curfreq = freq;
-
- outbits(16, RSF16_ENCODE(freq), fmi->io);
- outbits(8, 0xC0, fmi->io);
- msleep(143); /* was schedule_timeout(HZ/7) */
- mutex_unlock(&fmi->lock);
- if (!fmi->mute)
- fmi_unmute(fmi);
- return 0;
-}
-
static inline int fmi_getsigstr(struct fmi *fmi)
{
int val;
@@ -173,7 +162,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
return -EINVAL;
/* rounding in steps of 800 to match the freq
that will be used */
- fmi_setfreq(fmi, (f->frequency / 800) * 800);
+ lm7000_set_freq((f->frequency / 800) * 800, fmi, fmi_set_pins);
return 0;
}
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
new file mode 100644
index 000000000000..d0b6bb507634
--- /dev/null
+++ b/drivers/media/radio/radio-shark.c
@@ -0,0 +1,376 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver
+ *
+ * Note the radioSHARK offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark was taken from the small userspace
+ * shark.c program by Michael Rolig, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include <sound/tea575x-tuner.h>
+
+/*
+ * Version Information
+ */
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+#define SHARK_IN_EP 0x83
+#define SHARK_OUT_EP 0x05
+
+#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
+#define TEA575X_BIT_BAND_MASK (3<<20)
+#define TEA575X_BIT_BAND_FM (0<<20)
+
+#define TB_LEN 6
+#define DRV_NAME "radioshark"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+ enum led_brightness value);
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+ enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+ enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+ [BLUE_LED] = {
+ .name = "%s:blue:",
+ .brightness = LED_OFF,
+ .max_brightness = 127,
+ .brightness_set = shark_led_set_blue,
+ },
+ [BLUE_PULSE_LED] = {
+ .name = "%s:blue-pulse:",
+ .brightness = LED_OFF,
+ .max_brightness = 255,
+ .brightness_set = shark_led_set_blue_pulse,
+ },
+ [RED_LED] = {
+ .name = "%s:red:",
+ .brightness = LED_OFF,
+ .max_brightness = 1,
+ .brightness_set = shark_led_set_red,
+ },
+};
+
+struct shark_device {
+ struct usb_device *usbdev;
+ struct v4l2_device v4l2_dev;
+ struct snd_tea575x tea;
+
+ struct work_struct led_work;
+ struct led_classdev leds[NO_LEDS];
+ char led_names[NO_LEDS][32];
+ atomic_t brightness[NO_LEDS];
+ unsigned long brightness_new;
+
+ u8 *transfer_buffer;
+ u32 last_val;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static void shark_write_val(struct snd_tea575x *tea, u32 val)
+{
+ struct shark_device *shark = tea->private_data;
+ int i, res, actual_len;
+
+ /* Avoid unnecessary (slow) USB transfers */
+ if (shark->last_val == val)
+ return;
+
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
+ for (i = 0; i < 4; i++)
+ shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
+
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res >= 0)
+ shark->last_val = val;
+ else
+ v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
+}
+
+static u32 shark_read_val(struct snd_tea575x *tea)
+{
+ struct shark_device *shark = tea->private_data;
+ int i, res, actual_len;
+ u32 val = 0;
+
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ shark->transfer_buffer[0] = 0x80;
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0) {
+ v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
+ return shark->last_val;
+ }
+
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0) {
+ v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
+ return shark->last_val;
+ }
+
+ for (i = 0; i < 4; i++)
+ val |= shark->transfer_buffer[i] << (24 - i * 8);
+
+ shark->last_val = val;
+
+ /*
+ * The shark does not allow actually reading the stereo / mono pin :(
+ * So assume that when we're tuned to an FM station and mono has not
+ * been requested, that we're receiving stereo.
+ */
+ if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
+ !(val & TEA575X_BIT_MONO))
+ shark->tea.stereo = true;
+ else
+ shark->tea.stereo = false;
+
+ return val;
+}
+
+static struct snd_tea575x_ops shark_tea_ops = {
+ .write_val = shark_write_val,
+ .read_val = shark_read_val,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+ struct shark_device *shark =
+ container_of(work, struct shark_device, led_work);
+ int i, res, brightness, actual_len;
+
+ /*
+ * We use the v4l2_dev lock and registered bit to ensure the device
+ * does not get unplugged and unreffed while we're running.
+ */
+ mutex_lock(&shark->tea.mutex);
+ if (!video_is_registered(&shark->tea.vd))
+ goto leave;
+
+ for (i = 0; i < 3; i++) {
+ if (!test_and_clear_bit(i, &shark->brightness_new))
+ continue;
+
+ brightness = atomic_read(&shark->brightness[i]);
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ if (i != RED_LED) {
+ shark->transfer_buffer[0] = 0xA0 + i;
+ shark->transfer_buffer[1] = brightness;
+ } else
+ shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev, 0x05),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0)
+ v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+ shark->led_names[i], res);
+ }
+leave:
+ mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct shark_device *shark =
+ container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+ atomic_set(&shark->brightness[BLUE_LED], value);
+ set_bit(BLUE_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct shark_device *shark = container_of(led_cdev,
+ struct shark_device, leds[BLUE_PULSE_LED]);
+
+ atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
+ set_bit(BLUE_PULSE_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct shark_device *shark =
+ container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+ atomic_set(&shark->brightness[RED_LED], value);
+ set_bit(RED_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+ struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+ int i;
+
+ mutex_lock(&shark->tea.mutex);
+ v4l2_device_disconnect(&shark->v4l2_dev);
+ snd_tea575x_exit(&shark->tea);
+ mutex_unlock(&shark->tea.mutex);
+
+ for (i = 0; i < NO_LEDS; i++)
+ led_classdev_unregister(&shark->leds[i]);
+
+ v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+ cancel_work_sync(&shark->led_work);
+ v4l2_device_unregister(&shark->v4l2_dev);
+ kfree(shark->transfer_buffer);
+ kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct shark_device *shark;
+ int i, retval = -ENOMEM;
+
+ shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+ if (!shark)
+ return retval;
+
+ shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+ if (!shark->transfer_buffer)
+ goto err_alloc_buffer;
+
+ /*
+ * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+ * pointer in intfdata causing v4l2-device.c to not set it. Which
+ * results in usb_shark_disconnect() referencing the dangling pointer
+ *
+ * REMOVE (as soon as the above bug is fixed, patch submitted)
+ */
+ usb_set_intfdata(intf, NULL);
+
+ shark->v4l2_dev.release = usb_shark_release;
+ v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+ retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+ goto err_reg_dev;
+ }
+
+ shark->usbdev = interface_to_usbdev(intf);
+ shark->tea.v4l2_dev = &shark->v4l2_dev;
+ shark->tea.private_data = shark;
+ shark->tea.radio_nr = -1;
+ shark->tea.ops = &shark_tea_ops;
+ shark->tea.cannot_mute = true;
+ strlcpy(shark->tea.card, "Griffin radioSHARK",
+ sizeof(shark->tea.card));
+ usb_make_path(shark->usbdev, shark->tea.bus_info,
+ sizeof(shark->tea.bus_info));
+
+ retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
+ goto err_init_tea;
+ }
+
+ INIT_WORK(&shark->led_work, shark_led_work);
+ for (i = 0; i < NO_LEDS; i++) {
+ shark->leds[i] = shark_led_templates[i];
+ snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+ shark->leds[i].name, shark->v4l2_dev.name);
+ shark->leds[i].name = shark->led_names[i];
+ /*
+ * We don't fail the probe if we fail to register the leds,
+ * because once we've called snd_tea575x_init, the /dev/radio0
+ * node may be opened from userspace holding a reference to us!
+ *
+ * Note we cannot register the leds first instead as
+ * shark_led_work depends on the v4l2 mutex and registered bit.
+ */
+ retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+ if (retval)
+ v4l2_err(&shark->v4l2_dev,
+ "couldn't register led: %s\n",
+ shark->led_names[i]);
+ }
+
+ return 0;
+
+err_init_tea:
+ v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+ kfree(shark->transfer_buffer);
+err_alloc_buffer:
+ kfree(shark);
+
+ return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x077d,
+ .idProduct = 0x627a,
+ .bcdDevice_lo = 0x0001,
+ .bcdDevice_hi = 0x0001,
+ .bInterfaceClass = 3,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+ .name = DRV_NAME,
+ .probe = usb_shark_probe,
+ .disconnect = usb_shark_disconnect,
+ .id_table = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
new file mode 100644
index 000000000000..b9575de3e7e8
--- /dev/null
+++ b/drivers/media/radio/radio-shark2.c
@@ -0,0 +1,348 @@
+/*
+ * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver
+ *
+ * Note the radioSHARK2 offers the audio through a regular USB audio device,
+ * this driver only handles the tuning.
+ *
+ * The info necessary to drive the shark2 was taken from the small userspace
+ * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public
+ * Domain.
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-device.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+#define SHARK_IN_EP 0x83
+#define SHARK_OUT_EP 0x05
+
+#define TB_LEN 7
+#define DRV_NAME "radioshark2"
+
+#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
+
+enum { BLUE_LED, RED_LED, NO_LEDS };
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+ enum led_brightness value);
+static void shark_led_set_red(struct led_classdev *led_cdev,
+ enum led_brightness value);
+
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+ [BLUE_LED] = {
+ .name = "%s:blue:",
+ .brightness = LED_OFF,
+ .max_brightness = 127,
+ .brightness_set = shark_led_set_blue,
+ },
+ [RED_LED] = {
+ .name = "%s:red:",
+ .brightness = LED_OFF,
+ .max_brightness = 1,
+ .brightness_set = shark_led_set_red,
+ },
+};
+
+struct shark_device {
+ struct usb_device *usbdev;
+ struct v4l2_device v4l2_dev;
+ struct radio_tea5777 tea;
+
+ struct work_struct led_work;
+ struct led_classdev leds[NO_LEDS];
+ char led_names[NO_LEDS][32];
+ atomic_t brightness[NO_LEDS];
+ unsigned long brightness_new;
+
+ u8 *transfer_buffer;
+};
+
+static atomic_t shark_instance = ATOMIC_INIT(0);
+
+static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
+{
+ struct shark_device *shark = tea->private_data;
+ int i, res, actual_len;
+
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ shark->transfer_buffer[0] = 0x81; /* Write register command */
+ for (i = 0; i < 6; i++)
+ shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
+
+ v4l2_dbg(1, debug, tea->v4l2_dev,
+ "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
+ shark->transfer_buffer[0], shark->transfer_buffer[1],
+ shark->transfer_buffer[2], shark->transfer_buffer[3],
+ shark->transfer_buffer[4], shark->transfer_buffer[5],
+ shark->transfer_buffer[6]);
+
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0) {
+ v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
+ return res;
+ }
+
+ return 0;
+}
+
+static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
+{
+ struct shark_device *shark = tea->private_data;
+ int i, res, actual_len;
+ u32 reg = 0;
+
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ shark->transfer_buffer[0] = 0x82;
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0) {
+ v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
+ return res;
+ }
+
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0) {
+ v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
+ return res;
+ }
+
+ for (i = 0; i < 3; i++)
+ reg |= shark->transfer_buffer[i] << (16 - i * 8);
+
+ v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
+ shark->transfer_buffer[0], shark->transfer_buffer[1],
+ shark->transfer_buffer[2]);
+
+ *reg_ret = reg;
+ return 0;
+}
+
+static struct radio_tea5777_ops shark_tea_ops = {
+ .write_reg = shark_write_reg,
+ .read_reg = shark_read_reg,
+};
+
+static void shark_led_work(struct work_struct *work)
+{
+ struct shark_device *shark =
+ container_of(work, struct shark_device, led_work);
+ int i, res, brightness, actual_len;
+ /*
+ * We use the v4l2_dev lock and registered bit to ensure the device
+ * does not get unplugged and unreffed while we're running.
+ */
+ mutex_lock(&shark->tea.mutex);
+ if (!video_is_registered(&shark->tea.vd))
+ goto leave;
+
+ for (i = 0; i < 2; i++) {
+ if (!test_and_clear_bit(i, &shark->brightness_new))
+ continue;
+
+ brightness = atomic_read(&shark->brightness[i]);
+ memset(shark->transfer_buffer, 0, TB_LEN);
+ shark->transfer_buffer[0] = 0x83 + i;
+ shark->transfer_buffer[1] = brightness;
+ res = usb_interrupt_msg(shark->usbdev,
+ usb_sndintpipe(shark->usbdev,
+ SHARK_OUT_EP),
+ shark->transfer_buffer, TB_LEN,
+ &actual_len, 1000);
+ if (res < 0)
+ v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
+ shark->led_names[i], res);
+ }
+leave:
+ mutex_unlock(&shark->tea.mutex);
+}
+
+static void shark_led_set_blue(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct shark_device *shark =
+ container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
+
+ atomic_set(&shark->brightness[BLUE_LED], value);
+ set_bit(BLUE_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
+
+static void shark_led_set_red(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct shark_device *shark =
+ container_of(led_cdev, struct shark_device, leds[RED_LED]);
+
+ atomic_set(&shark->brightness[RED_LED], value);
+ set_bit(RED_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
+
+static void usb_shark_disconnect(struct usb_interface *intf)
+{
+ struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+ int i;
+
+ mutex_lock(&shark->tea.mutex);
+ v4l2_device_disconnect(&shark->v4l2_dev);
+ radio_tea5777_exit(&shark->tea);
+ mutex_unlock(&shark->tea.mutex);
+
+ for (i = 0; i < NO_LEDS; i++)
+ led_classdev_unregister(&shark->leds[i]);
+
+ v4l2_device_put(&shark->v4l2_dev);
+}
+
+static void usb_shark_release(struct v4l2_device *v4l2_dev)
+{
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+ cancel_work_sync(&shark->led_work);
+ v4l2_device_unregister(&shark->v4l2_dev);
+ kfree(shark->transfer_buffer);
+ kfree(shark);
+}
+
+static int usb_shark_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct shark_device *shark;
+ int i, retval = -ENOMEM;
+
+ shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
+ if (!shark)
+ return retval;
+
+ shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
+ if (!shark->transfer_buffer)
+ goto err_alloc_buffer;
+
+ /*
+ * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
+ * pointer in intfdata causing v4l2-device.c to not set it. Which
+ * results in usb_shark_disconnect() referencing the dangling pointer
+ *
+ * REMOVE (as soon as the above bug is fixed, patch submitted)
+ */
+ usb_set_intfdata(intf, NULL);
+
+ shark->v4l2_dev.release = usb_shark_release;
+ v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+ retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
+ goto err_reg_dev;
+ }
+
+ shark->usbdev = interface_to_usbdev(intf);
+ shark->tea.v4l2_dev = &shark->v4l2_dev;
+ shark->tea.private_data = shark;
+ shark->tea.ops = &shark_tea_ops;
+ shark->tea.has_am = true;
+ shark->tea.write_before_read = true;
+ strlcpy(shark->tea.card, "Griffin radioSHARK2",
+ sizeof(shark->tea.card));
+ usb_make_path(shark->usbdev, shark->tea.bus_info,
+ sizeof(shark->tea.bus_info));
+
+ retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
+ goto err_init_tea;
+ }
+
+ INIT_WORK(&shark->led_work, shark_led_work);
+ for (i = 0; i < NO_LEDS; i++) {
+ shark->leds[i] = shark_led_templates[i];
+ snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+ shark->leds[i].name, shark->v4l2_dev.name);
+ shark->leds[i].name = shark->led_names[i];
+ /*
+ * We don't fail the probe if we fail to register the leds,
+ * because once we've called radio_tea5777_init, the /dev/radio0
+ * node may be opened from userspace holding a reference to us!
+ *
+ * Note we cannot register the leds first instead as
+ * shark_led_work depends on the v4l2 mutex and registered bit.
+ */
+ retval = led_classdev_register(&intf->dev, &shark->leds[i]);
+ if (retval)
+ v4l2_err(&shark->v4l2_dev,
+ "couldn't register led: %s\n",
+ shark->led_names[i]);
+ }
+
+ return 0;
+
+err_init_tea:
+ v4l2_device_unregister(&shark->v4l2_dev);
+err_reg_dev:
+ kfree(shark->transfer_buffer);
+err_alloc_buffer:
+ kfree(shark);
+
+ return retval;
+}
+
+/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
+static struct usb_device_id usb_shark_device_table[] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x077d,
+ .idProduct = 0x627a,
+ .bcdDevice_lo = 0x0010,
+ .bcdDevice_hi = 0x0010,
+ .bInterfaceClass = 3,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
+
+static struct usb_driver usb_shark_driver = {
+ .name = DRV_NAME,
+ .probe = usb_shark_probe,
+ .disconnect = usb_shark_disconnect,
+ .id_table = usb_shark_device_table,
+};
+module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c
new file mode 100644
index 000000000000..5bc9fa62720b
--- /dev/null
+++ b/drivers/media/radio/radio-tea5777.c
@@ -0,0 +1,491 @@
+/*
+ * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ *
+ * 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/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <asm/div64.h>
+#include "radio-tea5777.h"
+
+MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
+MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
+MODULE_LICENSE("GPL");
+
+/* Fixed FM only band for now, will implement multi-band support when the
+ VIDIOC_ENUM_FREQ_BANDS API is upstream */
+#define TEA5777_FM_RANGELOW (76000 * 16)
+#define TEA5777_FM_RANGEHIGH (108000 * 16)
+
+#define TEA5777_FM_IF 150 /* kHz */
+#define TEA5777_FM_FREQ_STEP 50 /* kHz */
+
+/* Write reg, common bits */
+#define TEA5777_W_MUTE_MASK (1LL << 47)
+#define TEA5777_W_MUTE_SHIFT 47
+#define TEA5777_W_AM_FM_MASK (1LL << 46)
+#define TEA5777_W_AM_FM_SHIFT 46
+#define TEA5777_W_STB_MASK (1LL << 45)
+#define TEA5777_W_STB_SHIFT 45
+
+#define TEA5777_W_IFCE_MASK (1LL << 29)
+#define TEA5777_W_IFCE_SHIFT 29
+#define TEA5777_W_IFW_MASK (1LL << 28)
+#define TEA5777_W_IFW_SHIFT 28
+#define TEA5777_W_HILO_MASK (1LL << 27)
+#define TEA5777_W_HILO_SHIFT 27
+#define TEA5777_W_DBUS_MASK (1LL << 26)
+#define TEA5777_W_DBUS_SHIFT 26
+
+#define TEA5777_W_INTEXT_MASK (1LL << 24)
+#define TEA5777_W_INTEXT_SHIFT 24
+#define TEA5777_W_P1_MASK (1LL << 23)
+#define TEA5777_W_P1_SHIFT 23
+#define TEA5777_W_P0_MASK (1LL << 22)
+#define TEA5777_W_P0_SHIFT 22
+#define TEA5777_W_PEN1_MASK (1LL << 21)
+#define TEA5777_W_PEN1_SHIFT 21
+#define TEA5777_W_PEN0_MASK (1LL << 20)
+#define TEA5777_W_PEN0_SHIFT 20
+
+#define TEA5777_W_CHP0_MASK (1LL << 18)
+#define TEA5777_W_CHP0_SHIFT 18
+#define TEA5777_W_DEEM_MASK (1LL << 17)
+#define TEA5777_W_DEEM_SHIFT 17
+
+#define TEA5777_W_SEARCH_MASK (1LL << 7)
+#define TEA5777_W_SEARCH_SHIFT 7
+#define TEA5777_W_PROGBLIM_MASK (1LL << 6)
+#define TEA5777_W_PROGBLIM_SHIFT 6
+#define TEA5777_W_UPDWN_MASK (1LL << 5)
+#define TEA5777_W_UPDWN_SHIFT 5
+#define TEA5777_W_SLEV_MASK (3LL << 3)
+#define TEA5777_W_SLEV_SHIFT 3
+
+/* Write reg, FM specific bits */
+#define TEA5777_W_FM_PLL_MASK (0x1fffLL << 32)
+#define TEA5777_W_FM_PLL_SHIFT 32
+#define TEA5777_W_FM_FREF_MASK (0x03LL << 30)
+#define TEA5777_W_FM_FREF_SHIFT 30
+#define TEA5777_W_FM_FREF_VALUE 0 /* 50 kHz tune steps, 150 kHz IF */
+
+#define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15)
+#define TEA5777_W_FM_FORCEMONO_SHIFT 15
+#define TEA5777_W_FM_SDSOFF_MASK (1LL << 14)
+#define TEA5777_W_FM_SDSOFF_SHIFT 14
+#define TEA5777_W_FM_DOFF_MASK (1LL << 13)
+#define TEA5777_W_FM_DOFF_SHIFT 13
+
+#define TEA5777_W_FM_STEP_MASK (3LL << 1)
+#define TEA5777_W_FM_STEP_SHIFT 1
+
+/* Write reg, AM specific bits */
+#define TEA5777_W_AM_PLL_MASK (0x7ffLL << 34)
+#define TEA5777_W_AM_PLL_SHIFT 34
+#define TEA5777_W_AM_AGCRF_MASK (1LL << 33)
+#define TEA5777_W_AM_AGCRF_SHIFT 33
+#define TEA5777_W_AM_AGCIF_MASK (1LL << 32)
+#define TEA5777_W_AM_AGCIF_SHIFT 32
+#define TEA5777_W_AM_MWLW_MASK (1LL << 31)
+#define TEA5777_W_AM_MWLW_SHIFT 31
+#define TEA5777_W_AM_LNA_MASK (1LL << 30)
+#define TEA5777_W_AM_LNA_SHIFT 30
+
+#define TEA5777_W_AM_PEAK_MASK (1LL << 25)
+#define TEA5777_W_AM_PEAK_SHIFT 25
+
+#define TEA5777_W_AM_RFB_MASK (1LL << 16)
+#define TEA5777_W_AM_RFB_SHIFT 16
+#define TEA5777_W_AM_CALLIGN_MASK (1LL << 15)
+#define TEA5777_W_AM_CALLIGN_SHIFT 15
+#define TEA5777_W_AM_CBANK_MASK (0x7fLL << 8)
+#define TEA5777_W_AM_CBANK_SHIFT 8
+
+#define TEA5777_W_AM_DELAY_MASK (1LL << 2)
+#define TEA5777_W_AM_DELAY_SHIFT 2
+#define TEA5777_W_AM_STEP_MASK (1LL << 1)
+#define TEA5777_W_AM_STEP_SHIFT 1
+
+/* Read reg, common bits */
+#define TEA5777_R_LEVEL_MASK (0x0f << 17)
+#define TEA5777_R_LEVEL_SHIFT 17
+#define TEA5777_R_SFOUND_MASK (0x01 << 16)
+#define TEA5777_R_SFOUND_SHIFT 16
+#define TEA5777_R_BLIM_MASK (0x01 << 15)
+#define TEA5777_R_BLIM_SHIFT 15
+
+/* Read reg, FM specific bits */
+#define TEA5777_R_FM_STEREO_MASK (0x01 << 21)
+#define TEA5777_R_FM_STEREO_SHIFT 21
+#define TEA5777_R_FM_PLL_MASK 0x1fff
+#define TEA5777_R_FM_PLL_SHIFT 0
+
+static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
+{
+ return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
+}
+
+static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
+{
+ u64 freq;
+ int res;
+
+ freq = clamp_t(u32, tea->freq,
+ TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH) + 8;
+ do_div(freq, 16); /* to kHz */
+
+ freq -= TEA5777_FM_IF;
+ do_div(freq, TEA5777_FM_FREQ_STEP);
+
+ tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
+ tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
+ tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
+
+ res = tea->ops->write_reg(tea, tea->write_reg);
+ if (res)
+ return res;
+
+ tea->needs_write = false;
+ tea->read_reg = -1;
+ tea->freq = tea5777_freq_to_v4l2_freq(tea, freq);
+
+ return 0;
+}
+
+static int radio_tea5777_update_read_reg(struct radio_tea5777 *tea, int wait)
+{
+ int res;
+
+ if (tea->read_reg != -1)
+ return 0;
+
+ if (tea->write_before_read && tea->needs_write) {
+ res = radio_tea5777_set_freq(tea);
+ if (res)
+ return res;
+ }
+
+ if (wait) {
+ if (schedule_timeout_interruptible(msecs_to_jiffies(wait)))
+ return -ERESTARTSYS;
+ }
+
+ res = tea->ops->read_reg(tea, &tea->read_reg);
+ if (res)
+ return res;
+
+ tea->needs_write = true;
+ return 0;
+}
+
+/*
+ * Linux Video interface
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+
+ strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
+ strlcpy(v->card, tea->card, sizeof(v->card));
+ strlcat(v->card, " TEA5777", sizeof(v->card));
+ strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps |= V4L2_CAP_HW_FREQ_SEEK;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+ int res;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ res = radio_tea5777_update_read_reg(tea, 0);
+ if (res)
+ return res;
+
+ memset(v, 0, sizeof(*v));
+ if (tea->has_am)
+ strlcpy(v->name, "AM/FM", sizeof(v->name));
+ else
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+ v->rangelow = TEA5777_FM_RANGELOW;
+ v->rangehigh = TEA5777_FM_RANGEHIGH;
+ v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
+ V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
+ /* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
+ v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
+ (TEA5777_R_LEVEL_SHIFT - 12);
+
+ /* Invalidate read_reg, so that next call we return up2date signal */
+ tea->read_reg = -1;
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+
+ if (v->index)
+ return -EINVAL;
+
+ if (v->audmode == V4L2_TUNER_MODE_MONO)
+ tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
+ else
+ tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
+
+ return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = tea->freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ tea->freq = f->frequency;
+ return radio_tea5777_set_freq(tea);
+}
+
+static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
+ struct v4l2_hw_freq_seek *a)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+ u32 orig_freq = tea->freq;
+ unsigned long timeout;
+ int res, spacing = 200 * 16; /* 200 kHz */
+ /* These are fixed *for now* */
+ const u32 seek_rangelow = TEA5777_FM_RANGELOW;
+ const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
+
+ if (a->tuner || a->wrap_around)
+ return -EINVAL;
+
+ tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
+ if (seek_rangelow != tea->seek_rangelow) {
+ tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+ tea->freq = seek_rangelow;
+ res = radio_tea5777_set_freq(tea);
+ if (res)
+ goto leave;
+ tea->seek_rangelow = tea->freq;
+ }
+ if (seek_rangehigh != tea->seek_rangehigh) {
+ tea->write_reg |= TEA5777_W_UPDWN_MASK;
+ tea->freq = seek_rangehigh;
+ res = radio_tea5777_set_freq(tea);
+ if (res)
+ goto leave;
+ tea->seek_rangehigh = tea->freq;
+ }
+ tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+
+ tea->write_reg |= TEA5777_W_SEARCH_MASK;
+ if (a->seek_upward) {
+ tea->write_reg |= TEA5777_W_UPDWN_MASK;
+ tea->freq = orig_freq + spacing;
+ } else {
+ tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
+ tea->freq = orig_freq - spacing;
+ }
+ res = radio_tea5777_set_freq(tea);
+ if (res)
+ goto leave;
+
+ timeout = jiffies + msecs_to_jiffies(5000);
+ for (;;) {
+ if (time_after(jiffies, timeout)) {
+ res = -ENODATA;
+ break;
+ }
+
+ res = radio_tea5777_update_read_reg(tea, 100);
+ if (res)
+ break;
+
+ /*
+ * Note we use tea->freq to track how far we've searched sofar
+ * this is necessary to ensure we continue seeking at the right
+ * point, in the write_before_read case.
+ */
+ tea->freq = (tea->read_reg & TEA5777_R_FM_PLL_MASK);
+ tea->freq = tea5777_freq_to_v4l2_freq(tea, tea->freq);
+
+ if ((tea->read_reg & TEA5777_R_SFOUND_MASK)) {
+ tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+ return 0;
+ }
+
+ if (tea->read_reg & TEA5777_R_BLIM_MASK) {
+ res = -ENODATA;
+ break;
+ }
+
+ /* Force read_reg update */
+ tea->read_reg = -1;
+ }
+leave:
+ tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
+ tea->write_reg &= ~TEA5777_W_SEARCH_MASK;
+ tea->freq = orig_freq;
+ radio_tea5777_set_freq(tea);
+ return res;
+}
+
+static int tea575x_s_ctrl(struct v4l2_ctrl *c)
+{
+ struct radio_tea5777 *tea =
+ container_of(c->handler, struct radio_tea5777, ctrl_handler);
+
+ switch (c->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (c->val)
+ tea->write_reg |= TEA5777_W_MUTE_MASK;
+ else
+ tea->write_reg &= ~TEA5777_W_MUTE_MASK;
+
+ return radio_tea5777_set_freq(tea);
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_file_operations tea575x_fops = {
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+};
+
+static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device tea575x_radio = {
+ .ioctl_ops = &tea575x_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
+ .s_ctrl = tea575x_s_ctrl,
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
+{
+ int res;
+
+ tea->write_reg = (1LL << TEA5777_W_IFCE_SHIFT) |
+ (1LL << TEA5777_W_IFW_SHIFT) |
+ (1LL << TEA5777_W_INTEXT_SHIFT) |
+ (1LL << TEA5777_W_CHP0_SHIFT) |
+ (2LL << TEA5777_W_SLEV_SHIFT);
+ tea->freq = 90500 * 16; /* 90.5Mhz default */
+ res = radio_tea5777_set_freq(tea);
+ if (res) {
+ v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
+ return res;
+ }
+
+ tea->vd = tea575x_radio;
+ video_set_drvdata(&tea->vd, tea);
+ mutex_init(&tea->mutex);
+ strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
+ tea->vd.lock = &tea->mutex;
+ tea->vd.v4l2_dev = tea->v4l2_dev;
+ tea->fops = tea575x_fops;
+ tea->fops.owner = owner;
+ tea->vd.fops = &tea->fops;
+ set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
+
+ tea->vd.ctrl_handler = &tea->ctrl_handler;
+ v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
+ v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ res = tea->ctrl_handler.error;
+ if (res) {
+ v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
+ v4l2_ctrl_handler_free(&tea->ctrl_handler);
+ return res;
+ }
+ v4l2_ctrl_handler_setup(&tea->ctrl_handler);
+
+ res = video_register_device(&tea->vd, VFL_TYPE_RADIO, -1);
+ if (res) {
+ v4l2_err(tea->v4l2_dev, "can't register video device!\n");
+ v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+ return res;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_init);
+
+void radio_tea5777_exit(struct radio_tea5777 *tea)
+{
+ video_unregister_device(&tea->vd);
+ v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
+}
+EXPORT_SYMBOL_GPL(radio_tea5777_exit);
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
new file mode 100644
index 000000000000..55cbd78df5ed
--- /dev/null
+++ b/drivers/media/radio/radio-tea5777.h
@@ -0,0 +1,87 @@
+#ifndef __RADIO_TEA5777_H
+#define __RADIO_TEA5777_H
+
+/*
+ * v4l2 driver for TEA5777 Philips AM/FM radio tuner chips
+ *
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips:
+ *
+ * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#define TEA575X_FMIF 10700
+#define TEA575X_AMIF 450
+
+struct radio_tea5777;
+
+struct radio_tea5777_ops {
+ /*
+ * Write the 6 bytes large write register of the tea5777
+ *
+ * val represents the 6 write registers, with byte 1 from the
+ * datasheet being the most significant byte (so byte 5 of the u64),
+ * and byte 6 from the datasheet being the least significant byte.
+ *
+ * returns 0 on success.
+ */
+ int (*write_reg)(struct radio_tea5777 *tea, u64 val);
+ /*
+ * Read the 3 bytes large read register of the tea5777
+ *
+ * The read value gets returned in val, akin to write_reg, byte 1 from
+ * the datasheet is stored as the most significant byte (so byte 2 of
+ * the u32), and byte 3 from the datasheet gets stored as the least
+ * significant byte (iow byte 0 of the u32).
+ *
+ * returns 0 on success.
+ */
+ int (*read_reg)(struct radio_tea5777 *tea, u32 *val);
+};
+
+struct radio_tea5777 {
+ struct v4l2_device *v4l2_dev;
+ struct v4l2_file_operations fops;
+ struct video_device vd; /* video device */
+ bool has_am; /* Device can tune to AM freqs */
+ bool write_before_read; /* must write before read quirk */
+ bool needs_write; /* for write before read quirk */
+ u32 freq; /* current frequency */
+ u32 seek_rangelow; /* current hwseek limits */
+ u32 seek_rangehigh;
+ u32 read_reg;
+ u64 write_reg;
+ struct mutex mutex;
+ struct radio_tea5777_ops *ops;
+ void *private_data;
+ u8 card[32];
+ u8 bus_info[32];
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
+void radio_tea5777_exit(struct radio_tea5777 *tea);
+
+#endif /* __RADIO_TEA5777_H */
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index f1b607099b6c..e8428f573ccd 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1514,7 +1514,8 @@ static int wl1273_fm_vidioc_g_tuner(struct file *file, void *priv,
tuner->rangehigh = WL1273_FREQ(WL1273_BAND_OTHER_HIGH);
tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS |
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO;
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP;
if (radio->stereo)
tuner->audmode = V4L2_TUNER_MODE_STEREO;
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 969cf494d85b..9e38132afec6 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -4,6 +4,7 @@
* Driver for radios with Silicon Labs Si470x FM Radio Receivers
*
* Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.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
@@ -127,14 +128,6 @@ static unsigned short space = 2;
module_param(space, ushort, 0444);
MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76 - 108 MHz (Japan wide band) */
-/* 2: 76 - 90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
/* De-emphasis */
/* 0: 75 us (USA) */
/* 1: 50 us (Europe, Australia, Japan) */
@@ -152,19 +145,66 @@ static unsigned int seek_timeout = 5000;
module_param(seek_timeout, uint, 0644);
MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
-
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_WRAP,
+ .rangelow = 87500 * 16,
+ .rangehigh = 108000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_WRAP,
+ .rangelow = 76000 * 16,
+ .rangehigh = 108000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 2,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_WRAP,
+ .rangelow = 76000 * 16,
+ .rangehigh = 90000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+};
/**************************************************************************
* Generic Functions
**************************************************************************/
/*
+ * si470x_set_band - set the band
+ */
+static int si470x_set_band(struct si470x_device *radio, int band)
+{
+ if (radio->band == band)
+ return 0;
+
+ radio->band = band;
+ radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
+ radio->registers[SYSCONFIG2] |= radio->band << 6;
+ return si470x_set_register(radio, SYSCONFIG2);
+}
+
+/*
* si470x_set_chan - set the channel
*/
static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
{
int retval;
- unsigned long timeout;
bool timed_out = 0;
/* start tuning */
@@ -174,26 +214,12 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
if (retval < 0)
goto done;
- /* currently I2C driver only uses interrupt way to tune */
- if (radio->stci_enabled) {
- INIT_COMPLETION(radio->completion);
-
- /* wait till tune operation has completed */
- retval = wait_for_completion_timeout(&radio->completion,
- msecs_to_jiffies(tune_timeout));
- if (!retval)
- timed_out = true;
- } else {
- /* wait till tune operation has completed */
- timeout = jiffies + msecs_to_jiffies(tune_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- && (!timed_out));
- }
+ /* wait till tune operation has completed */
+ INIT_COMPLETION(radio->completion);
+ retval = wait_for_completion_timeout(&radio->completion,
+ msecs_to_jiffies(tune_timeout));
+ if (!retval)
+ timed_out = true;
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
dev_warn(&radio->videodev.dev, "tune does not complete\n");
@@ -201,7 +227,6 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
dev_warn(&radio->videodev.dev,
"tune timed out after %u ms\n", tune_timeout);
-stop:
/* stop tuning */
radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
retval = si470x_set_register(radio, CHANNEL);
@@ -210,48 +235,39 @@ done:
return retval;
}
-
/*
- * si470x_get_freq - get the frequency
+ * si470x_get_step - get channel spacing
*/
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+static unsigned int si470x_get_step(struct si470x_device *radio)
{
- unsigned int spacing, band_bottom;
- unsigned short chan;
- int retval;
-
/* Spacing (kHz) */
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
/* 0: 200 kHz (USA, Australia) */
case 0:
- spacing = 0.200 * FREQ_MUL; break;
+ return 200 * 16;
/* 1: 100 kHz (Europe, Japan) */
case 1:
- spacing = 0.100 * FREQ_MUL; break;
+ return 100 * 16;
/* 2: 50 kHz */
default:
- spacing = 0.050 * FREQ_MUL; break;
+ return 50 * 16;
};
+}
- /* Bottom of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL; break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL; break;
- };
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+ int chan, retval;
/* read channel */
retval = si470x_get_register(radio, READCHAN);
chan = radio->registers[READCHAN] & READCHAN_READCHAN;
/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
- *freq = chan * spacing + band_bottom;
+ *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
return retval;
}
@@ -262,44 +278,12 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
*/
int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
{
- unsigned int spacing, band_bottom, band_top;
unsigned short chan;
- /* Spacing (kHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
- /* 0: 200 kHz (USA, Australia) */
- case 0:
- spacing = 0.200 * FREQ_MUL; break;
- /* 1: 100 kHz (Europe, Japan) */
- case 1:
- spacing = 0.100 * FREQ_MUL; break;
- /* 2: 50 kHz */
- default:
- spacing = 0.050 * FREQ_MUL; break;
- };
-
- /* Bottom/Top of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL;
- band_top = 108 * FREQ_MUL;
- break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL;
- band_top = 108 * FREQ_MUL;
- break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL;
- band_top = 90 * FREQ_MUL;
- break;
- };
-
- freq = clamp(freq, band_bottom, band_top);
+ freq = clamp(freq, bands[radio->band].rangelow,
+ bands[radio->band].rangehigh);
/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
- chan = (freq - band_bottom) / spacing;
+ chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
return si470x_set_chan(radio, chan);
}
@@ -309,19 +293,43 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
* si470x_set_seek - set seek
*/
static int si470x_set_seek(struct si470x_device *radio,
- unsigned int wrap_around, unsigned int seek_upward)
+ struct v4l2_hw_freq_seek *seek)
{
- int retval = 0;
- unsigned long timeout;
+ int band, retval;
+ unsigned int freq;
bool timed_out = 0;
+ /* set band */
+ if (seek->rangelow || seek->rangehigh) {
+ for (band = 0; band < ARRAY_SIZE(bands); band++) {
+ if (bands[band].rangelow == seek->rangelow &&
+ bands[band].rangehigh == seek->rangehigh)
+ break;
+ }
+ if (band == ARRAY_SIZE(bands))
+ return -EINVAL; /* No matching band found */
+ } else
+ band = 1; /* If nothing is specified seek 76 - 108 Mhz */
+
+ if (radio->band != band) {
+ retval = si470x_get_freq(radio, &freq);
+ if (retval)
+ return retval;
+ retval = si470x_set_band(radio, band);
+ if (retval)
+ return retval;
+ retval = si470x_set_freq(radio, freq);
+ if (retval)
+ return retval;
+ }
+
/* start seeking */
radio->registers[POWERCFG] |= POWERCFG_SEEK;
- if (wrap_around == 1)
+ if (seek->wrap_around)
radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
else
radio->registers[POWERCFG] |= POWERCFG_SKMODE;
- if (seek_upward == 1)
+ if (seek->seek_upward)
radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
else
radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
@@ -329,26 +337,12 @@ static int si470x_set_seek(struct si470x_device *radio,
if (retval < 0)
return retval;
- /* currently I2C driver only uses interrupt way to seek */
- if (radio->stci_enabled) {
- INIT_COMPLETION(radio->completion);
-
- /* wait till seek operation has completed */
- retval = wait_for_completion_timeout(&radio->completion,
- msecs_to_jiffies(seek_timeout));
- if (!retval)
- timed_out = true;
- } else {
- /* wait till seek operation has completed */
- timeout = jiffies + msecs_to_jiffies(seek_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- && (!timed_out));
- }
+ /* wait till tune operation has completed */
+ INIT_COMPLETION(radio->completion);
+ retval = wait_for_completion_timeout(&radio->completion,
+ msecs_to_jiffies(seek_timeout));
+ if (!retval)
+ timed_out = true;
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
dev_warn(&radio->videodev.dev, "seek does not complete\n");
@@ -356,14 +350,13 @@ static int si470x_set_seek(struct si470x_device *radio,
dev_warn(&radio->videodev.dev,
"seek failed / band limit reached\n");
-stop:
/* stop seeking */
radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
retval = si470x_set_register(radio, POWERCFG);
/* try again, if timed out */
if (retval == 0 && timed_out)
- return -EAGAIN;
+ return -ENODATA;
return retval;
}
@@ -391,8 +384,8 @@ int si470x_start(struct si470x_device *radio)
/* sysconfig 2 */
radio->registers[SYSCONFIG2] =
- (0x3f << 8) | /* SEEKTH */
- ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
+ (0x1f << 8) | /* SEEKTH */
+ ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
15; /* VOLUME (max) */
retval = si470x_set_register(radio, SYSCONFIG2);
@@ -583,39 +576,26 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct si470x_device *radio = video_drvdata(file);
- int retval;
+ int retval = 0;
if (tuner->index != 0)
return -EINVAL;
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- return retval;
+ if (!radio->status_rssi_auto_update) {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ return retval;
+ }
/* driver constants */
strcpy(tuner->name, "FM");
tuner->type = V4L2_TUNER_RADIO;
tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
-
- /* range limits */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe, default) */
- default:
- tuner->rangelow = 87.5 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- case 1:
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 90 * FREQ_MUL;
- break;
- };
+ V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_WRAP;
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
/* stereo indicator == stereo (instead of mono) */
if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
@@ -698,10 +678,18 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct si470x_device *radio = video_drvdata(file);
+ int retval;
if (freq->tuner != 0)
return -EINVAL;
+ if (freq->frequency < bands[radio->band].rangelow ||
+ freq->frequency > bands[radio->band].rangehigh) {
+ /* Switch to band 1 which covers everything we support */
+ retval = si470x_set_band(radio, 1);
+ if (retval)
+ return retval;
+ }
return si470x_set_freq(radio, freq->frequency);
}
@@ -717,7 +705,21 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
if (seek->tuner != 0)
return -EINVAL;
- return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+ return si470x_set_seek(radio, seek);
+}
+
+/*
+ * si470x_vidioc_enum_freq_bands - enumerate supported bands
+ */
+static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ if (band->tuner != 0)
+ return -EINVAL;
+ if (band->index >= ARRAY_SIZE(bands))
+ return -EINVAL;
+ *band = bands[band->index];
+ return 0;
}
const struct v4l2_ctrl_ops si470x_ctrl_ops = {
@@ -734,6 +736,7 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
.vidioc_g_frequency = si470x_vidioc_g_frequency,
.vidioc_s_frequency = si470x_vidioc_s_frequency,
.vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
+ .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index a80044c5874e..643a6ff7c5d0 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -350,7 +350,9 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
}
radio->client = client;
+ radio->band = 1; /* Default to 76 - 108 MHz */
mutex_init(&radio->lock);
+ init_completion(&radio->completion);
/* video device initialization */
radio->videodev = si470x_viddev_template;
@@ -406,10 +408,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
radio->rd_index = 0;
init_waitqueue_head(&radio->read_queue);
- /* mark Seek/Tune Complete Interrupt enabled */
- radio->stci_enabled = true;
- init_completion(&radio->completion);
-
retval = request_threaded_irq(client->irq, NULL, si470x_i2c_interrupt,
IRQF_TRIGGER_FALLING, DRIVER_NAME, radio);
if (retval) {
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index f412f7ab270b..146be4263ea1 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -143,7 +143,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
* Software/Hardware Versions from Scratch Page
**************************************************************************/
#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
-#define RADIO_SW_VERSION 7
+#define RADIO_SW_VERSION 1
#define RADIO_HW_VERSION 1
@@ -399,12 +399,19 @@ static void si470x_int_in_callback(struct urb *urb)
}
}
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ /* Sometimes the device returns len 0 packets */
+ if (urb->actual_length != RDS_REPORT_SIZE)
goto resubmit;
- if (urb->actual_length > 0) {
+ radio->registers[STATUSRSSI] =
+ get_unaligned_be16(&radio->int_in_buffer[1]);
+
+ if (radio->registers[STATUSRSSI] & STATUSRSSI_STC)
+ complete(&radio->completion);
+
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) {
/* Update RDS registers with URB data */
- for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+ for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++)
radio->registers[STATUSRSSI + regnr] =
get_unaligned_be16(&radio->int_in_buffer[
regnr * RADIO_REGISTER_SIZE + 1]);
@@ -480,6 +487,7 @@ resubmit:
radio->int_in_running = 0;
}
}
+ radio->status_rssi_auto_update = radio->int_in_running;
}
@@ -534,13 +542,6 @@ static int si470x_start_usb(struct si470x_device *radio)
{
int retval;
- /* start radio */
- retval = si470x_start(radio);
- if (retval < 0)
- return retval;
-
- v4l2_ctrl_handler_setup(&radio->hdl);
-
/* initialize interrupt urb */
usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
usb_rcvintpipe(radio->usbdev,
@@ -560,6 +561,15 @@ static int si470x_start_usb(struct si470x_device *radio)
"submitting int urb failed (%d)\n", retval);
radio->int_in_running = 0;
}
+ radio->status_rssi_auto_update = radio->int_in_running;
+
+ /* start radio */
+ retval = si470x_start(radio);
+ if (retval < 0)
+ return retval;
+
+ v4l2_ctrl_handler_setup(&radio->hdl);
+
return retval;
}
@@ -587,7 +597,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
}
radio->usbdev = interface_to_usbdev(intf);
radio->intf = intf;
+ radio->band = 1; /* Default to 76 - 108 MHz */
mutex_init(&radio->lock);
+ init_completion(&radio->completion);
iface_desc = intf->cur_altsetting;
@@ -698,9 +710,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
"linux-media@vger.kernel.org\n");
}
- /* set initial frequency */
- si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
/* set led to connect state */
si470x_set_led_state(radio, BLINK_GREEN_LED);
@@ -723,6 +732,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
if (retval < 0)
goto err_all;
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
/* register video device */
retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr);
@@ -781,11 +793,16 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
static int si470x_usb_driver_resume(struct usb_interface *intf)
{
struct si470x_device *radio = usb_get_intfdata(intf);
+ int ret;
dev_info(&intf->dev, "resuming now...\n");
/* start radio */
- return si470x_start_usb(radio);
+ ret = si470x_start_usb(radio);
+ if (ret == 0)
+ v4l2_ctrl_handler_setup(&radio->hdl);
+
+ return ret;
}
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 4921cab8e0fa..2f089b4252df 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -87,7 +87,7 @@
#define SYSCONFIG2 5 /* System Configuration 2 */
#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
+#define SYSCONFIG2_BAND 0x00c0 /* bits 07..06: Band Select */
#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
@@ -147,6 +147,7 @@ struct si470x_device {
struct v4l2_device v4l2_dev;
struct video_device videodev;
struct v4l2_ctrl_handler hdl;
+ int band;
/* Silabs internal registers (0..15) */
unsigned short registers[RADIO_REGISTER_NUM];
@@ -160,7 +161,7 @@ struct si470x_device {
unsigned int wr_index;
struct completion completion;
- bool stci_enabled; /* Seek/Tune Complete Interrupt */
+ bool status_rssi_auto_update; /* Does RSSI get updated automatic? */
#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
/* reference to USB and video device */
@@ -189,7 +190,7 @@ struct si470x_device {
* Firmware Versions
**************************************************************************/
-#define RADIO_FW_VERSION 15
+#define RADIO_FW_VERSION 12
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c
index 43fb72291bea..3dd9fc097c47 100644
--- a/drivers/media/radio/wl128x/fmdrv_rx.c
+++ b/drivers/media/radio/wl128x/fmdrv_rx.c
@@ -251,7 +251,7 @@ again:
if (!timeleft) {
fmerr("Timeout(%d sec),didn't get tune ended int\n",
jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
- return -ETIMEDOUT;
+ return -ENODATA;
}
int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 080b96a61f1a..49a11ec1f449 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -285,7 +285,9 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv,
tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO |
((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0);
tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
- V4L2_TUNER_CAP_LOW;
+ V4L2_TUNER_CAP_LOW |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_WRAP;
tuner->audmode = (stereo_mono_mode ?
V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO);
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index f97eeb870455..5180390be7ab 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -1,21 +1,20 @@
-menuconfig RC_CORE
- tristate "Remote Controller adapters"
+config RC_CORE
+ tristate
+ depends on MEDIA_RC_SUPPORT
depends on INPUT
- default INPUT
- ---help---
- Enable support for Remote Controllers on Linux. This is
- needed in order to support several video capture adapters,
- standalone IR receivers/transmitters, and RF receivers.
+ default y
- Enable this option if you have a video capture board even
- if you don't need IR, as otherwise, you may not be able to
- compile the driver for your adapter.
+source "drivers/media/rc/keymaps/Kconfig"
-if RC_CORE
+menuconfig RC_DECODERS
+ bool "Remote controller decoders"
+ depends on RC_CORE
+ default y
+if RC_DECODERS
config LIRC
- tristate
- default y
+ tristate "LIRC interface driver"
+ depends on RC_CORE
---help---
Enable this option to build the Linux Infrared Remote
@@ -24,7 +23,16 @@ config LIRC
LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
-source "drivers/media/rc/keymaps/Kconfig"
+config IR_LIRC_CODEC
+ tristate "Enable IR to LIRC bridge"
+ depends on RC_CORE
+ depends on LIRC
+ default y
+
+ ---help---
+ Enable this option to pass raw IR to and from userspace via
+ the LIRC interface.
+
config IR_NEC_DECODER
tristate "Enable IR raw decoder for the NEC protocol"
@@ -108,16 +116,13 @@ config IR_MCE_KBD_DECODER
Enable this option if you have a Microsoft Remote Keyboard for
Windows Media Center Edition, which you would like to use with
a raw IR receiver in your system.
+endif #RC_DECODERS
-config IR_LIRC_CODEC
- tristate "Enable IR to LIRC bridge"
+menuconfig RC_DEVICES
+ bool "Remote Controller devices"
depends on RC_CORE
- depends on LIRC
- default y
- ---help---
- Enable this option to pass raw IR to and from userspace via
- the LIRC interface.
+if RC_DEVICES
config RC_ATI_REMOTE
tristate "ATI / X10 based USB RF remote controls"
@@ -254,6 +259,17 @@ config IR_WINBOND_CIR
To compile this driver as a module, choose M here: the module will
be called winbond_cir.
+config IR_IGUANA
+ tristate "IguanaWorks USB IR Transceiver"
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
+ Both infrared receive and send are supported.
+
+ To compile this driver as a module, choose M here: the module will
+ be called iguanair.
+
config RC_LOOPBACK
tristate "Remote Control Loopback Driver"
depends on RC_CORE
@@ -276,4 +292,4 @@ config IR_GPIO_CIR
To compile this driver as a module, choose M here: the module will
be called gpio-ir-recv.
-endif #RC_CORE
+endif #RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 29f364f88a94..f871d1986c21 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 7be377fc1be8..8fa72e2dacb1 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -147,7 +147,8 @@ static bool mouse = true;
module_param(mouse, bool, 0444);
MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
-#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#define dbginfo(dev, format, arg...) \
+ do { if (debug) dev_info(dev , format , ## arg); } while (0)
#undef err
#define err(format, arg...) printk(KERN_ERR format , ## arg)
@@ -191,17 +192,41 @@ static const char *get_medion_keymap(struct usb_interface *interface)
return RC_MAP_MEDION_X10;
}
-static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 };
-static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap };
-static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
+static const struct ati_receiver_type type_ati = {
+ .default_keymap = RC_MAP_ATI_X10
+};
+static const struct ati_receiver_type type_medion = {
+ .get_default_keymap = get_medion_keymap
+};
+static const struct ati_receiver_type type_firefly = {
+ .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
+};
static struct usb_device_id ati_remote_table[] = {
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_ati
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_medion
+ },
+ {
+ USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
+ .driver_info = (unsigned long)&type_firefly
+ },
{} /* Terminating entry */
};
@@ -296,25 +321,8 @@ static const struct {
{KIND_END, 0x00, EV_MAX + 1, 0, 0}
};
-/* Local function prototypes */
-static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
-static void ati_remote_irq_out (struct urb *urb);
-static void ati_remote_irq_in (struct urb *urb);
-static void ati_remote_input_report (struct urb *urb);
-static int ati_remote_initialize (struct ati_remote *ati_remote);
-static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
-static void ati_remote_disconnect (struct usb_interface *interface);
-
-/* usb specific object to register with the usb subsystem */
-static struct usb_driver ati_remote_driver = {
- .name = "ati_remote",
- .probe = ati_remote_probe,
- .disconnect = ati_remote_disconnect,
- .id_table = ati_remote_table,
-};
-
/*
- * ati_remote_dump_input
+ * ati_remote_dump_input
*/
static void ati_remote_dump(struct device *dev, unsigned char *data,
unsigned int len)
@@ -326,12 +334,14 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
data[0], data[1], data[2], data[3]);
else
- dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
- len, data[0], data[1], data[2], data[3], data[4], data[5]);
+ dev_warn(dev,
+ "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
+ len, data[0], data[1], data[2], data[3], data[4],
+ data[5]);
}
/*
- * ati_remote_open
+ * ati_remote_open
*/
static int ati_remote_open(struct ati_remote *ati_remote)
{
@@ -355,7 +365,7 @@ out: mutex_unlock(&ati_remote->open_mutex);
}
/*
- * ati_remote_close
+ * ati_remote_close
*/
static void ati_remote_close(struct ati_remote *ati_remote)
{
@@ -390,7 +400,7 @@ static void ati_remote_rc_close(struct rc_dev *rdev)
}
/*
- * ati_remote_irq_out
+ * ati_remote_irq_out
*/
static void ati_remote_irq_out(struct urb *urb)
{
@@ -408,11 +418,12 @@ static void ati_remote_irq_out(struct urb *urb)
}
/*
- * ati_remote_sendpacket
+ * ati_remote_sendpacket
*
- * Used to send device initialization strings
+ * Used to send device initialization strings
*/
-static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
+ unsigned char *data)
{
int retval = 0;
@@ -441,7 +452,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
}
/*
- * ati_remote_compute_accel
+ * ati_remote_compute_accel
*
* Implements acceleration curve for directional control pad
* If elapsed time since last event is > 1/4 second, user "stopped",
@@ -478,7 +489,7 @@ static int ati_remote_compute_accel(struct ati_remote *ati_remote)
}
/*
- * ati_remote_report_input
+ * ati_remote_report_input
*/
static void ati_remote_input_report(struct urb *urb)
{
@@ -518,7 +529,8 @@ static void ati_remote_input_report(struct urb *urb)
remote_num = (data[3] >> 4) & 0x0f;
if (channel_mask & (1 << (remote_num + 1))) {
dbginfo(&ati_remote->interface->dev,
- "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+ "Masked input from channel 0x%02x: data %02x,%02x, "
+ "mask= 0x%02lx\n",
remote_num, data[1], data[2], channel_mask);
return;
}
@@ -546,7 +558,9 @@ static void ati_remote_input_report(struct urb *urb)
if (wheel_keycode == KEY_RESERVED) {
/* scrollwheel was not mapped, assume mouse */
- /* Look up event code index in the mouse translation table. */
+ /* Look up event code index in the mouse translation
+ * table.
+ */
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
if (scancode == ati_remote_tbl[i].data) {
index = i;
@@ -630,9 +644,9 @@ static void ati_remote_input_report(struct urb *urb)
} else {
/*
- * Other event kinds are from the directional control pad, and have an
- * acceleration factor applied to them. Without this acceleration, the
- * control pad is mostly unusable.
+ * Other event kinds are from the directional control pad, and
+ * have an acceleration factor applied to them. Without this
+ * acceleration, the control pad is mostly unusable.
*/
acc = ati_remote_compute_accel(ati_remote);
@@ -659,7 +673,8 @@ static void ati_remote_input_report(struct urb *urb)
input_report_rel(dev, REL_Y, acc);
break;
default:
- dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+ dev_dbg(&ati_remote->interface->dev,
+ "ati_remote kind=%d\n",
ati_remote_tbl[index].kind);
}
input_sync(dev);
@@ -670,7 +685,7 @@ static void ati_remote_input_report(struct urb *urb)
}
/*
- * ati_remote_irq_in
+ * ati_remote_irq_in
*/
static void ati_remote_irq_in(struct urb *urb)
{
@@ -684,22 +699,25 @@ static void ati_remote_irq_in(struct urb *urb)
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
- dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+ dev_dbg(&ati_remote->interface->dev,
+ "%s: urb error status, unlink?\n",
__func__);
return;
default: /* error */
- dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
+ dev_dbg(&ati_remote->interface->dev,
+ "%s: Nonzero urb status %d\n",
__func__, urb->status);
}
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+ dev_err(&ati_remote->interface->dev,
+ "%s: usb_submit_urb()=%d\n",
__func__, retval);
}
/*
- * ati_remote_alloc_buffers
+ * ati_remote_alloc_buffers
*/
static int ati_remote_alloc_buffers(struct usb_device *udev,
struct ati_remote *ati_remote)
@@ -726,7 +744,7 @@ static int ati_remote_alloc_buffers(struct usb_device *udev,
}
/*
- * ati_remote_free_buffers
+ * ati_remote_free_buffers
*/
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
{
@@ -825,9 +843,10 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
}
/*
- * ati_remote_probe
+ * ati_remote_probe
*/
-static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+static int ati_remote_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *iface_host = interface->cur_altsetting;
@@ -949,7 +968,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
}
/*
- * ati_remote_disconnect
+ * ati_remote_disconnect
*/
static void ati_remote_disconnect(struct usb_interface *interface)
{
@@ -971,6 +990,14 @@ static void ati_remote_disconnect(struct usb_interface *interface)
kfree(ati_remote);
}
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+ .name = "ati_remote",
+ .probe = ati_remote_probe,
+ .disconnect = ati_remote_disconnect,
+ .id_table = ati_remote_table,
+};
+
module_usb_driver(ati_remote_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index bef5296173c9..647dd951b0e8 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1018,6 +1018,8 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
spin_lock_init(&dev->hw_lock);
+ dev->hw_io = pnp_port_start(pnp_dev, 0);
+
pnp_set_drvdata(pnp_dev, dev);
dev->pnp_dev = pnp_dev;
@@ -1072,7 +1074,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
/* claim the resources */
error = -EBUSY;
- dev->hw_io = pnp_port_start(pnp_dev, 0);
if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
dev->hw_io = -1;
dev->irq = -1;
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 6aabf7ae3a31..ab30c64f8124 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -23,6 +23,8 @@
* USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -110,30 +112,32 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
return val;
}
-#define pr_reg(text, ...) \
- printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
/* dump current cir register contents */
static void cir_dump_regs(struct fintek_dev *fintek)
{
fintek_config_mode_enable(fintek);
fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
- pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
- pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
- (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
+ pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
+ pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+ (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
- pr_reg(" * CR CIR IRQ NUM: 0x%x\n",
- fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
+ pr_info(" * CR CIR IRQ NUM: 0x%x\n",
+ fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
fintek_config_mode_disable(fintek);
- pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
- pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS));
- pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL));
- pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA));
- pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
- pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA));
+ pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
+ pr_info(" * STATUS: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_STATUS));
+ pr_info(" * CONTROL: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_CONTROL));
+ pr_info(" * RX_DATA: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_RX_DATA));
+ pr_info(" * TX_CONTROL: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
+ pr_info(" * TX_DATA: 0x%x\n",
+ fintek_cir_reg_read(fintek, CIR_TX_DATA));
}
/* detect hardware features */
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 0d875450c5ce..04cb272db16a 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -82,12 +82,21 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
goto err_allocate_device;
}
+ rcdev->priv = gpio_dev;
rcdev->driver_type = RC_DRIVER_IR_RAW;
- rcdev->allowed_protos = RC_TYPE_ALL;
rcdev->input_name = GPIO_IR_DEVICE_NAME;
+ rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
rcdev->input_id.bustype = BUS_HOST;
+ rcdev->input_id.vendor = 0x0001;
+ rcdev->input_id.product = 0x0001;
+ rcdev->input_id.version = 0x0100;
+ rcdev->dev.parent = &pdev->dev;
rcdev->driver_name = GPIO_IR_DRIVER_NAME;
- rcdev->map_name = RC_MAP_EMPTY;
+ if (pdata->allowed_protos)
+ rcdev->allowed_protos = pdata->allowed_protos;
+ else
+ rcdev->allowed_protos = RC_TYPE_ALL;
+ rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = pdata->gpio_nr;
@@ -188,18 +197,7 @@ static struct platform_driver gpio_ir_recv_driver = {
#endif
},
};
-
-static int __init gpio_ir_recv_init(void)
-{
- return platform_driver_register(&gpio_ir_recv_driver);
-}
-module_init(gpio_ir_recv_init);
-
-static void __exit gpio_ir_recv_exit(void)
-{
- platform_driver_unregister(&gpio_ir_recv_driver);
-}
-module_exit(gpio_ir_recv_exit);
+module_platform_driver(gpio_ir_recv_driver);
MODULE_DESCRIPTION("GPIO IR Receiver driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
new file mode 100644
index 000000000000..5e2eaf8ba73e
--- /dev/null
+++ b/drivers/media/rc/iguanair.c
@@ -0,0 +1,639 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+
+struct iguanair {
+ struct rc_dev *rc;
+
+ struct device *dev;
+ struct usb_device *udev;
+
+ int pipe_in, pipe_out;
+ uint8_t bufsize;
+ uint8_t version[2];
+
+ struct mutex lock;
+
+ /* receiver support */
+ bool receiver_on;
+ dma_addr_t dma_in;
+ uint8_t *buf_in;
+ struct urb *urb_in;
+ struct completion completion;
+
+ /* transmit support */
+ bool tx_overflow;
+ uint32_t carrier;
+ uint8_t cycle_overhead;
+ uint8_t channels;
+ uint8_t busy4;
+ uint8_t busy7;
+
+ char name[64];
+ char phys[64];
+};
+
+#define CMD_GET_VERSION 0x01
+#define CMD_GET_BUFSIZE 0x11
+#define CMD_GET_FEATURES 0x10
+#define CMD_SEND 0x15
+#define CMD_EXECUTE 0x1f
+#define CMD_RX_OVERFLOW 0x31
+#define CMD_TX_OVERFLOW 0x32
+#define CMD_RECEIVER_ON 0x12
+#define CMD_RECEIVER_OFF 0x14
+
+#define DIR_IN 0xdc
+#define DIR_OUT 0xcd
+
+#define MAX_PACKET_SIZE 8u
+#define TIMEOUT 1000
+
+struct packet {
+ uint16_t start;
+ uint8_t direction;
+ uint8_t cmd;
+};
+
+struct response_packet {
+ struct packet header;
+ uint8_t data[4];
+};
+
+struct send_packet {
+ struct packet header;
+ uint8_t length;
+ uint8_t channels;
+ uint8_t busy7;
+ uint8_t busy4;
+ uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+ if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+ switch (ir->buf_in[3]) {
+ case CMD_TX_OVERFLOW:
+ ir->tx_overflow = true;
+ case CMD_RECEIVER_OFF:
+ case CMD_RECEIVER_ON:
+ case CMD_SEND:
+ complete(&ir->completion);
+ break;
+ case CMD_RX_OVERFLOW:
+ dev_warn(ir->dev, "receive overflow\n");
+ break;
+ default:
+ dev_warn(ir->dev, "control code %02x received\n",
+ ir->buf_in[3]);
+ break;
+ }
+ } else if (len >= 7) {
+ DEFINE_IR_RAW_EVENT(rawir);
+ unsigned i;
+
+ init_ir_raw_event(&rawir);
+
+ for (i = 0; i < 7; i++) {
+ if (ir->buf_in[i] == 0x80) {
+ rawir.pulse = false;
+ rawir.duration = US_TO_NS(21845);
+ } else {
+ rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+ rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+ 21330;
+ }
+
+ ir_raw_event_store_with_filter(ir->rc, &rawir);
+ }
+
+ ir_raw_event_handle(ir->rc);
+ }
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+ struct iguanair *ir;
+
+ if (!urb)
+ return;
+
+ ir = urb->context;
+ if (!ir) {
+ usb_unlink_urb(urb);
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ process_ir_data(ir, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
+ struct response_packet *response, unsigned *res_len)
+{
+ unsigned offset, len;
+ int rc, transferred;
+
+ for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
+ len = min(size - offset, MAX_PACKET_SIZE);
+
+ if (ir->tx_overflow)
+ return -EOVERFLOW;
+
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
+ len, &transferred, TIMEOUT);
+ if (rc)
+ return rc;
+
+ if (transferred != len)
+ return -EIO;
+ }
+
+ if (response) {
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
+ sizeof(*response), res_len, TIMEOUT);
+ }
+
+ return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+ struct packet packet;
+ struct response_packet response;
+ int rc, len;
+
+ packet.start = 0;
+ packet.direction = DIR_OUT;
+ packet.cmd = CMD_GET_VERSION;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get version\n");
+ goto out;
+ }
+
+ if (len != 6) {
+ dev_info(ir->dev, "failed to get version\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ ir->version[0] = response.data[0];
+ ir->version[1] = response.data[1];
+ ir->bufsize = 150;
+ ir->cycle_overhead = 65;
+
+ packet.cmd = CMD_GET_BUFSIZE;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get buffer size\n");
+ goto out;
+ }
+
+ if (len != 5) {
+ dev_info(ir->dev, "failed to get buffer size\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ ir->bufsize = response.data[0];
+
+ if (ir->version[0] == 0 || ir->version[1] == 0)
+ goto out;
+
+ packet.cmd = CMD_GET_FEATURES;
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ if (rc) {
+ dev_info(ir->dev, "failed to get features\n");
+ goto out;
+ }
+
+ if (len < 5) {
+ dev_info(ir->dev, "failed to get features\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ if (len > 5 && ir->version[0] >= 4)
+ ir->cycle_overhead = response.data[1];
+
+out:
+ return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+ struct packet packet = { 0, DIR_OUT, enable ?
+ CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
+ int rc;
+
+ INIT_COMPLETION(ir->completion);
+
+ rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
+ if (rc)
+ return rc;
+
+ wait_for_completion_timeout(&ir->completion, TIMEOUT);
+
+ return 0;
+}
+
+/*
+ * The iguana ir creates the carrier by busy spinning after each pulse or
+ * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays.
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (carrier < 25000 || carrier > 150000)
+ return -EINVAL;
+
+ mutex_lock(&ir->lock);
+
+ if (carrier != ir->carrier) {
+ uint32_t cycles, fours, sevens;
+
+ ir->carrier = carrier;
+
+ cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+ ir->cycle_overhead;
+
+ /* make up the the remainer of 4-cycle blocks */
+ switch (cycles & 3) {
+ case 0:
+ sevens = 0;
+ break;
+ case 1:
+ sevens = 3;
+ break;
+ case 2:
+ sevens = 2;
+ break;
+ case 3:
+ sevens = 1;
+ break;
+ }
+
+ fours = (cycles - sevens * 7) / 4;
+
+ /* magic happens here */
+ ir->busy7 = (4 - sevens) * 2;
+ ir->busy4 = 110 - fours;
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+ struct iguanair *ir = dev->priv;
+
+ if (mask > 15)
+ return 4;
+
+ mutex_lock(&ir->lock);
+ ir->channels = mask;
+ mutex_unlock(&ir->lock);
+
+ return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+ struct iguanair *ir = dev->priv;
+ uint8_t space, *payload;
+ unsigned i, size, rc;
+ struct send_packet *packet;
+
+ mutex_lock(&ir->lock);
+
+ /* convert from us to carrier periods */
+ for (i = size = 0; i < count; i++) {
+ txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+ size += (txbuf[i] + 126) / 127;
+ }
+
+ packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+ if (!packet) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (size > ir->bufsize) {
+ rc = -E2BIG;
+ goto out;
+ }
+
+ packet->header.start = 0;
+ packet->header.direction = DIR_OUT;
+ packet->header.cmd = CMD_SEND;
+ packet->length = size;
+ packet->channels = ir->channels << 4;
+ packet->busy7 = ir->busy7;
+ packet->busy4 = ir->busy4;
+
+ space = 0;
+ payload = packet->payload;
+
+ for (i = 0; i < count; i++) {
+ unsigned periods = txbuf[i];
+
+ while (periods > 127) {
+ *payload++ = 127 | space;
+ periods -= 127;
+ }
+
+ *payload++ = periods | space;
+ space ^= 0x80;
+ }
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, false);
+ if (rc) {
+ dev_warn(ir->dev, "disable receiver before transmit failed\n");
+ goto out;
+ }
+ }
+
+ ir->tx_overflow = false;
+
+ INIT_COMPLETION(ir->completion);
+
+ rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
+
+ if (rc == 0) {
+ wait_for_completion_timeout(&ir->completion, TIMEOUT);
+ if (ir->tx_overflow)
+ rc = -EOVERFLOW;
+ }
+
+ ir->tx_overflow = false;
+
+ if (ir->receiver_on) {
+ if (iguanair_receiver(ir, true))
+ dev_warn(ir->dev, "re-enable receiver after transmit failed\n");
+ }
+
+out:
+ mutex_unlock(&ir->lock);
+ kfree(packet);
+
+ return rc;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ usb_submit_urb(ir->urb_in, GFP_KERNEL);
+
+ BUG_ON(ir->receiver_on);
+
+ rc = iguanair_receiver(ir, true);
+ if (rc == 0)
+ ir->receiver_on = true;
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+ struct iguanair *ir = rdev->priv;
+ int rc;
+
+ mutex_lock(&ir->lock);
+
+ rc = iguanair_receiver(ir, false);
+ ir->receiver_on = false;
+ if (rc)
+ dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+ usb_kill_urb(ir->urb_in);
+
+ mutex_unlock(&ir->lock);
+}
+
+static int __devinit iguanair_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct iguanair *ir;
+ struct rc_dev *rc;
+ int ret;
+ struct usb_host_interface *idesc;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+ &ir->dma_in);
+ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!ir->buf_in || !ir->urb_in) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ idesc = intf->altsetting;
+
+ if (idesc->desc.bNumEndpoints < 2) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ir->rc = rc;
+ ir->dev = &intf->dev;
+ ir->udev = udev;
+ ir->pipe_in = usb_rcvintpipe(udev,
+ idesc->endpoint[0].desc.bEndpointAddress);
+ ir->pipe_out = usb_sndintpipe(udev,
+ idesc->endpoint[1].desc.bEndpointAddress);
+ mutex_init(&ir->lock);
+ init_completion(&ir->completion);
+
+ ret = iguanair_get_features(ir);
+ if (ret) {
+ dev_warn(&intf->dev, "failed to get device features");
+ goto out;
+ }
+
+ usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
+ MAX_PACKET_SIZE, iguanair_rx, ir,
+ idesc->endpoint[0].desc.bInterval);
+ ir->urb_in->transfer_dma = ir->dma_in;
+ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ snprintf(ir->name, sizeof(ir->name),
+ "IguanaWorks USB IR Transceiver version %d.%d",
+ ir->version[0], ir->version[1]);
+
+ usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ usb_to_input_id(ir->udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc->allowed_protos = RC_TYPE_ALL;
+ rc->priv = ir;
+ rc->open = iguanair_open;
+ rc->close = iguanair_close;
+ rc->s_tx_mask = iguanair_set_tx_mask;
+ rc->s_tx_carrier = iguanair_set_tx_carrier;
+ rc->tx_ir = iguanair_tx;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_EMPTY;
+
+ iguanair_set_tx_carrier(rc, 38000);
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(&intf->dev, "failed to register rc device %d", ret);
+ goto out;
+ }
+
+ usb_set_intfdata(intf, ir);
+
+ dev_info(&intf->dev, "Registered %s", ir->name);
+
+ return 0;
+out:
+ if (ir) {
+ usb_free_urb(ir->urb_in);
+ usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in,
+ ir->dma_in);
+ }
+ rc_free_device(rc);
+ kfree(ir);
+ return ret;
+}
+
+static void __devexit iguanair_disconnect(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_kill_urb(ir->urb_in);
+ usb_free_urb(ir->urb_in);
+ usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
+ rc_unregister_device(ir->rc);
+ kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, false);
+ if (rc)
+ dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+ struct iguanair *ir = usb_get_intfdata(intf);
+ int rc = 0;
+
+ mutex_lock(&ir->lock);
+
+ if (ir->receiver_on) {
+ rc = iguanair_receiver(ir, true);
+ if (rc)
+ dev_warn(ir->dev, "failed to enable receiver after resume\n");
+ }
+
+ mutex_unlock(&ir->lock);
+
+ return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+ { USB_DEVICE(0x1781, 0x0938) },
+ { }
+};
+
+static struct usb_driver iguanair_driver = {
+ .name = DRIVER_NAME,
+ .probe = iguanair_probe,
+ .disconnect = __devexit_p(iguanair_disconnect),
+ .suspend = iguanair_suspend,
+ .resume = iguanair_resume,
+ .reset_resume = iguanair_resume,
+ .id_table = iguanair_table
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 84e06d3aa696..f38d9a8c6880 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -199,6 +199,7 @@ static bool debug;
#define VENDOR_REALTEK 0x0bda
#define VENDOR_TIVO 0x105a
#define VENDOR_CONEXANT 0x0572
+#define VENDOR_TWISTEDMELON 0x2596
enum mceusb_model_type {
MCE_GEN2 = 0, /* Most boards */
@@ -391,6 +392,12 @@ static struct usb_device_id mceusb_dev_table[] = {
/* Conexant Hybrid TV RDU253S Polaris */
{ USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
.driver_info = CX_HYBRID_TV },
+ /* Twisted Melon Inc. - Manta Mini Receiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
+ /* Twisted Melon Inc. - Manta Pico Receiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
+ /* Twisted Melon Inc. - Manta Transceiver */
+ { USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
/* Terminating entry */
{ }
};
@@ -410,14 +417,12 @@ struct mceusb_dev {
/* usb */
struct usb_device *usbdev;
struct urb *urb_in;
- struct usb_endpoint_descriptor *usb_ep_in;
struct usb_endpoint_descriptor *usb_ep_out;
/* buffers and dma */
unsigned char *buf_in;
unsigned int len_in;
dma_addr_t dma_in;
- dma_addr_t dma_out;
enum {
CMD_HEADER = 0,
@@ -686,7 +691,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
dev_info(dev, "Raw IR data, %d pulse/space samples\n", ir->rem);
}
-static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb)
{
struct mceusb_dev *ir;
int len;
@@ -733,7 +738,7 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
pipe = usb_sndintpipe(ir->usbdev,
ir->usb_ep_out->bEndpointAddress);
usb_fill_int_urb(async_urb, ir->usbdev, pipe,
- async_buf, size, (usb_complete_t)mce_async_callback,
+ async_buf, size, mce_async_callback,
ir, ir->usb_ep_out->bInterval);
memcpy(async_buf, data, size);
@@ -1031,7 +1036,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
ir_raw_event_handle(ir->rc);
}
-static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
+static void mceusb_dev_recv(struct urb *urb)
{
struct mceusb_dev *ir;
int buf_len;
@@ -1331,7 +1336,6 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
ir->model = model;
/* Saving usb interface data for use by the transmitter routine */
- ir->usb_ep_in = ep_in;
ir->usb_ep_out = ep_out;
if (dev->descriptor.iManufacturer
@@ -1349,8 +1353,8 @@ static int __devinit mceusb_dev_probe(struct usb_interface *intf,
goto rc_dev_fail;
/* wire up inbound data handler */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
- maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
+ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+ mceusb_dev_recv, ir, ep_in->bInterval);
ir->urb_in->transfer_dma = ir->dma_in;
ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index dc8a7dddccd4..699eef39128b 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -25,6 +25,8 @@
* USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
@@ -123,43 +125,40 @@ static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
return val;
}
-#define pr_reg(text, ...) \
- printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
/* dump current cir register contents */
static void cir_dump_regs(struct nvt_dev *nvt)
{
nvt_efm_enable(nvt);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
- pr_reg("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
- pr_reg(" * CR CIR ACTIVE : 0x%x\n",
- nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
- pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
- (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+ pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
+ pr_info(" * CR CIR ACTIVE : 0x%x\n",
+ nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+ pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+ (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
- pr_reg(" * CR CIR IRQ NUM: 0x%x\n",
- nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+ pr_info(" * CR CIR IRQ NUM: 0x%x\n",
+ nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
nvt_efm_disable(nvt);
- pr_reg("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
- pr_reg(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
- pr_reg(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
- pr_reg(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
- pr_reg(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
- pr_reg(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
- pr_reg(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
- pr_reg(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
- pr_reg(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
- pr_reg(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
- pr_reg(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
- pr_reg(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
- pr_reg(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
- pr_reg(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
- pr_reg(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
- pr_reg(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
- pr_reg(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
+ pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
+ pr_info(" * IRCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
+ pr_info(" * IRSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
+ pr_info(" * IREN: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
+ pr_info(" * RXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
+ pr_info(" * CP: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
+ pr_info(" * CC: 0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
+ pr_info(" * SLCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
+ pr_info(" * SLCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
+ pr_info(" * FIFOCON: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
+ pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
+ pr_info(" * SRXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
+ pr_info(" * TXFCONT: 0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
+ pr_info(" * STXFIFO: 0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
+ pr_info(" * FCCH: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
+ pr_info(" * FCCL: 0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
+ pr_info(" * IRFSM: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
}
/* dump current cir wake register contents */
@@ -170,59 +169,59 @@ static void cir_wake_dump_regs(struct nvt_dev *nvt)
nvt_efm_enable(nvt);
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
- pr_reg("%s: Dump CIR WAKE logical device registers:\n",
- NVT_DRIVER_NAME);
- pr_reg(" * CR CIR WAKE ACTIVE : 0x%x\n",
- nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
- pr_reg(" * CR CIR WAKE BASE ADDR: 0x%x\n",
- (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+ pr_info("%s: Dump CIR WAKE logical device registers:\n",
+ NVT_DRIVER_NAME);
+ pr_info(" * CR CIR WAKE ACTIVE : 0x%x\n",
+ nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+ pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n",
+ (nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
- pr_reg(" * CR CIR WAKE IRQ NUM: 0x%x\n",
- nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+ pr_info(" * CR CIR WAKE IRQ NUM: 0x%x\n",
+ nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
nvt_efm_disable(nvt);
- pr_reg("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
- pr_reg(" * IRCON: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
- pr_reg(" * IRSTS: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
- pr_reg(" * IREN: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
- pr_reg(" * FIFO CMP DEEP: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
- pr_reg(" * FIFO CMP TOL: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
- pr_reg(" * FIFO COUNT: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
- pr_reg(" * SLCH: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
- pr_reg(" * SLCL: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
- pr_reg(" * FIFOCON: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
- pr_reg(" * SRXFSTS: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
- pr_reg(" * SAMPLE RX FIFO: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
- pr_reg(" * WR FIFO DATA: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
- pr_reg(" * RD FIFO ONLY: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
- pr_reg(" * RD FIFO ONLY IDX: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
- pr_reg(" * FIFO IGNORE: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
- pr_reg(" * IRFSM: 0x%x\n",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
+ pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
+ pr_info(" * IRCON: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
+ pr_info(" * IRSTS: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
+ pr_info(" * IREN: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
+ pr_info(" * FIFO CMP DEEP: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
+ pr_info(" * FIFO CMP TOL: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
+ pr_info(" * FIFO COUNT: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
+ pr_info(" * SLCH: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
+ pr_info(" * SLCL: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
+ pr_info(" * FIFOCON: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
+ pr_info(" * SRXFSTS: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
+ pr_info(" * SAMPLE RX FIFO: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
+ pr_info(" * WR FIFO DATA: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
+ pr_info(" * RD FIFO ONLY: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+ pr_info(" * RD FIFO ONLY IDX: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
+ pr_info(" * FIFO IGNORE: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
+ pr_info(" * IRFSM: 0x%x\n",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
- pr_reg("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
- pr_reg("* Contents = ");
+ pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
+ pr_info("* Contents =");
for (i = 0; i < fifo_len; i++)
- printk(KERN_CONT "%02x ",
- nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
- printk(KERN_CONT "\n");
+ pr_cont(" %02x",
+ nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+ pr_cont("\n");
}
/* detect hardware features */
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 6e16b09c24a9..cabc19c10515 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -775,10 +775,11 @@ static ssize_t show_protocols(struct device *device,
if (dev->driver_type == RC_DRIVER_SCANCODE) {
enabled = dev->rc_map.rc_type;
allowed = dev->allowed_protos;
- } else {
+ } else if (dev->raw) {
enabled = dev->raw->enabled_protocols;
allowed = ir_raw_get_allowed_protocols();
- }
+ } else
+ return -ENODEV;
IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
(long long)allowed,
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 99937c94d7df..c128fac0ce2c 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -5,7 +5,7 @@
config VIDEO_V4L2
tristate
depends on VIDEO_DEV && VIDEO_V4L2_COMMON
- default VIDEO_DEV && VIDEO_V4L2_COMMON
+ default y
config VIDEOBUF_GEN
tristate
@@ -73,6 +73,7 @@ config VIDEOBUF2_DMA_SG
menuconfig VIDEO_CAPTURE_DRIVERS
bool "Video capture adapters"
depends on VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT
default y
---help---
Say Y here to enable selecting the video adapters for
@@ -461,6 +462,15 @@ config VIDEO_ADV7343
To compile this driver as a module, choose M here: the
module will be called adv7343.
+config VIDEO_ADV7393
+ tristate "ADV7393 video encoder"
+ depends on I2C
+ help
+ Support for Analog Devices I2C bus based ADV7393 encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7393.
+
config VIDEO_AK881X
tristate "AK8813/AK8814 video encoders"
depends on I2C
@@ -478,6 +488,7 @@ config VIDEO_SMIAPP_PLL
config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV7670 VGA camera. It currently only works with the M88ALP01
@@ -486,6 +497,7 @@ config VIDEO_OV7670
config VIDEO_VS6624
tristate "ST VS6624 sensor support"
depends on VIDEO_V4L2 && I2C
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the ST VS6624
camera.
@@ -496,6 +508,7 @@ config VIDEO_VS6624
config VIDEO_MT9M032
tristate "MT9M032 camera sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
select VIDEO_APTINA_PLL
---help---
This driver supports MT9M032 camera sensors from Aptina, monochrome
@@ -504,6 +517,7 @@ config VIDEO_MT9M032
config VIDEO_MT9P031
tristate "Aptina MT9P031 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
select VIDEO_APTINA_PLL
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
@@ -512,6 +526,7 @@ config VIDEO_MT9P031
config VIDEO_MT9T001
tristate "Aptina MT9T001 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt0t001 3 Mpixel camera.
@@ -519,6 +534,7 @@ config VIDEO_MT9T001
config VIDEO_MT9V011
tristate "Micron mt9v011 sensor support"
depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Micron
mt0v011 1.3 Mpixel camera. It currently only works with the
@@ -527,6 +543,7 @@ config VIDEO_MT9V011
config VIDEO_MT9V032
tristate "Micron MT9V032 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Micron
MT9V032 752x480 CMOS sensor.
@@ -534,6 +551,7 @@ config VIDEO_MT9V032
config VIDEO_TCM825X
tristate "TCM825x camera sensor support"
depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the Toshiba TCM825x VGA camera sensor.
It is used for example in Nokia N800.
@@ -541,12 +559,14 @@ config VIDEO_TCM825X
config VIDEO_SR030PC30
tristate "Siliconfile SR030PC30 sensor support"
depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This driver supports SR030PC30 VGA camera from Siliconfile
config VIDEO_NOON010PC30
tristate "Siliconfile NOON010PC30 sensor support"
depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This driver supports NOON010PC30 CIF camera from Siliconfile
@@ -554,6 +574,7 @@ source "drivers/media/video/m5mols/Kconfig"
config VIDEO_S5K6AA
tristate "Samsung S5K6AAFX sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
---help---
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
@@ -566,6 +587,7 @@ comment "Flash devices"
config VIDEO_ADP1653
tristate "ADP1653 flash support"
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the ADP1653 flash controller. It is used for
example in Nokia N900.
@@ -573,6 +595,7 @@ config VIDEO_ADP1653
config VIDEO_AS3645A
tristate "AS3645A flash driver support"
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the AS3645A and LM3555 flash controllers. It has
build in control for flash, torch and indicator LEDs.
@@ -647,30 +670,14 @@ menuconfig V4L_USB_DRIVERS
depends on USB
default y
-if V4L_USB_DRIVERS
+if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
-source "drivers/media/video/au0828/Kconfig"
+ comment "Webcam devices"
source "drivers/media/video/uvc/Kconfig"
source "drivers/media/video/gspca/Kconfig"
-source "drivers/media/video/pvrusb2/Kconfig"
-
-source "drivers/media/video/hdpvr/Kconfig"
-
-source "drivers/media/video/em28xx/Kconfig"
-
-source "drivers/media/video/tlg2300/Kconfig"
-
-source "drivers/media/video/cx231xx/Kconfig"
-
-source "drivers/media/video/tm6000/Kconfig"
-
-source "drivers/media/video/usbvision/Kconfig"
-
-source "drivers/media/video/sn9c102/Kconfig"
-
source "drivers/media/video/pwc/Kconfig"
source "drivers/media/video/cpia2/Kconfig"
@@ -711,15 +718,46 @@ config USB_S2255
Say Y here if you want support for the Sensoray 2255 USB device.
This driver can be compiled as a module, called s2255drv.
+source "drivers/media/video/sn9c102/Kconfig"
+
+endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
+
+if V4L_USB_DRIVERS
+
+ comment "Webcam and/or TV USB devices"
+
+source "drivers/media/video/em28xx/Kconfig"
+
+endif
+
+if V4L_USB_DRIVERS && MEDIA_ANALOG_TV_SUPPORT
+
+ comment "TV USB devices"
+
+source "drivers/media/video/au0828/Kconfig"
+
+source "drivers/media/video/pvrusb2/Kconfig"
+
+source "drivers/media/video/hdpvr/Kconfig"
+
+source "drivers/media/video/tlg2300/Kconfig"
+
+source "drivers/media/video/cx231xx/Kconfig"
+
+source "drivers/media/video/tm6000/Kconfig"
+
+source "drivers/media/video/usbvision/Kconfig"
+
endif # V4L_USB_DRIVERS
#
-# PCI drivers configuration
+# PCI drivers configuration - No devices here are for webcams
#
menuconfig V4L_PCI_DRIVERS
bool "V4L PCI(e) devices"
depends on PCI
+ depends on MEDIA_ANALOG_TV_SUPPORT
default y
---help---
Say Y here to enable support for these PCI(e) drivers.
@@ -814,11 +852,13 @@ endif # V4L_PCI_DRIVERS
#
# ISA & parallel port drivers configuration
+# All devices here are webcam or grabber devices
#
menuconfig V4L_ISA_PARPORT_DRIVERS
bool "V4L ISA and parallel port devices"
depends on ISA || PARPORT
+ depends on MEDIA_CAMERA_SUPPORT
default n
---help---
Say Y here to enable support for these ISA and parallel port drivers.
@@ -871,8 +911,13 @@ config VIDEO_W9966
endif # V4L_ISA_PARPORT_DRIVERS
+#
+# Platform drivers
+# All drivers here are currently for webcam support
+
menuconfig V4L_PLATFORM_DRIVERS
bool "V4L platform devices"
+ depends on MEDIA_CAMERA_SUPPORT
default n
---help---
Say Y here to enable support for platform-specific V4L drivers.
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index d209de0e0ca8..b7da9faa3b0a 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
+obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index 174bffacf117..45ecf8db1eae 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -26,11 +26,10 @@
#include <media/v4l2-ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/mutex.h>
-#define DRIVER_NAME "adv7180"
-
#define ADV7180_INPUT_CONTROL_REG 0x00
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
@@ -55,21 +54,21 @@
#define ADV7180_AUTODETECT_ENABLE_REG 0x07
#define ADV7180_AUTODETECT_DEFAULT 0x7f
-
+/* Contrast */
#define ADV7180_CON_REG 0x08 /*Unsigned */
-#define CON_REG_MIN 0
-#define CON_REG_DEF 128
-#define CON_REG_MAX 255
-
+#define ADV7180_CON_MIN 0
+#define ADV7180_CON_DEF 128
+#define ADV7180_CON_MAX 255
+/* Brightness*/
#define ADV7180_BRI_REG 0x0a /*Signed */
-#define BRI_REG_MIN -128
-#define BRI_REG_DEF 0
-#define BRI_REG_MAX 127
-
+#define ADV7180_BRI_MIN -128
+#define ADV7180_BRI_DEF 0
+#define ADV7180_BRI_MAX 127
+/* Hue */
#define ADV7180_HUE_REG 0x0b /*Signed, inverted */
-#define HUE_REG_MIN -127
-#define HUE_REG_DEF 0
-#define HUE_REG_MAX 128
+#define ADV7180_HUE_MIN -127
+#define ADV7180_HUE_DEF 0
+#define ADV7180_HUE_MAX 128
#define ADV7180_ADI_CTRL_REG 0x0e
#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
@@ -98,12 +97,12 @@
#define ADV7180_ICONF1_ACTIVE_LOW 0x01
#define ADV7180_ICONF1_PSYNC_ONLY 0x10
#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
-
+/* Saturation */
#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */
#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */
-#define SAT_REG_MIN 0
-#define SAT_REG_DEF 128
-#define SAT_REG_MAX 255
+#define ADV7180_SAT_MIN 0
+#define ADV7180_SAT_DEF 128
+#define ADV7180_SAT_MAX 255
#define ADV7180_IRQ1_LOCK 0x01
#define ADV7180_IRQ1_UNLOCK 0x02
@@ -121,18 +120,18 @@
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
struct adv7180_state {
+ struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_subdev sd;
struct work_struct work;
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
- s8 brightness;
- s16 hue;
- u8 contrast;
- u8 saturation;
u8 input;
};
+#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
+ struct adv7180_state, \
+ ctrl_hdl)->sd)
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
{
@@ -237,7 +236,7 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
if (ret)
return ret;
- /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+ /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
* all inputs and let the card driver take care of validation
*/
if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
@@ -316,117 +315,39 @@ out:
return ret;
}
-static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX,
- 1, BRI_REG_DEF);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX,
- 1, HUE_REG_DEF);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX,
- 1, CON_REG_DEF);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX,
- 1, SAT_REG_DEF);
- default:
- break;
- }
-
- return -EINVAL;
-}
-
-static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct adv7180_state *state = to_state(sd);
- int ret = mutex_lock_interruptible(&state->mutex);
- if (ret)
- return ret;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->brightness;
- break;
- case V4L2_CID_HUE:
- ctrl->value = state->hue;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = state->saturation;
- break;
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&state->mutex);
- return ret;
-}
-
-static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
struct adv7180_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = mutex_lock_interruptible(&state->mutex);
+ int val;
+
if (ret)
return ret;
-
+ val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- if ((ctrl->value > BRI_REG_MAX)
- || (ctrl->value < BRI_REG_MIN)) {
- ret = -ERANGE;
- break;
- }
- state->brightness = ctrl->value;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_BRI_REG,
- state->brightness);
+ ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
break;
case V4L2_CID_HUE:
- if ((ctrl->value > HUE_REG_MAX)
- || (ctrl->value < HUE_REG_MIN)) {
- ret = -ERANGE;
- break;
- }
- state->hue = ctrl->value;
/*Hue is inverted according to HSL chart */
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_HUE_REG, -state->hue);
+ ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
break;
case V4L2_CID_CONTRAST:
- if ((ctrl->value > CON_REG_MAX)
- || (ctrl->value < CON_REG_MIN)) {
- ret = -ERANGE;
- break;
- }
- state->contrast = ctrl->value;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_CON_REG,
- state->contrast);
+ ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
break;
case V4L2_CID_SATURATION:
- if ((ctrl->value > SAT_REG_MAX)
- || (ctrl->value < SAT_REG_MIN)) {
- ret = -ERANGE;
- break;
- }
/*
*This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
*Let's not confuse the user, everybody understands saturation
*/
- state->saturation = ctrl->value;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_SD_SAT_CB_REG,
- state->saturation);
+ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
+ val);
if (ret < 0)
break;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_SD_SAT_CR_REG,
- state->saturation);
+ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
+ val);
break;
default:
ret = -EINVAL;
@@ -436,6 +357,42 @@ static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
return ret;
}
+static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
+ .s_ctrl = adv7180_s_ctrl,
+};
+
+static int adv7180_init_controls(struct adv7180_state *state)
+{
+ v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
+ ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+ V4L2_CID_CONTRAST, ADV7180_CON_MIN,
+ ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+ V4L2_CID_SATURATION, ADV7180_SAT_MIN,
+ ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
+ V4L2_CID_HUE, ADV7180_HUE_MIN,
+ ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
+ state->sd.ctrl_handler = &state->ctrl_hdl;
+ if (state->ctrl_hdl.error) {
+ int err = state->ctrl_hdl.error;
+
+ v4l2_ctrl_handler_free(&state->ctrl_hdl);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+ return 0;
+}
+static void adv7180_exit_controls(struct adv7180_state *state)
+{
+ v4l2_ctrl_handler_free(&state->ctrl_hdl);
+}
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
@@ -445,9 +402,9 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.g_chip_ident = adv7180_g_chip_ident,
.s_std = adv7180_s_std,
- .queryctrl = adv7180_queryctrl,
- .g_ctrl = adv7180_g_ctrl,
- .s_ctrl = adv7180_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -539,7 +496,7 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
/* register for interrupts */
if (state->irq > 0) {
- ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+ ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
state);
if (ret)
return ret;
@@ -580,31 +537,6 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
return ret;
}
- /*Set default value for controls */
- ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG,
- state->brightness);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG,
- state->contrast);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
- state->saturation);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
- state->saturation);
- if (ret < 0)
- return ret;
-
return 0;
}
@@ -632,25 +564,26 @@ static __devinit int adv7180_probe(struct i2c_client *client,
INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
- state->brightness = BRI_REG_DEF;
- state->hue = HUE_REG_DEF;
- state->contrast = CON_REG_DEF;
- state->saturation = SAT_REG_DEF;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
- ret = init_device(client, state);
- if (0 != ret)
+ ret = adv7180_init_controls(state);
+ if (ret)
goto err_unreg_subdev;
+ ret = init_device(client, state);
+ if (ret)
+ goto err_free_ctrl;
return 0;
+err_free_ctrl:
+ adv7180_exit_controls(state);
err_unreg_subdev:
mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
kfree(state);
err:
- printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+ printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
}
@@ -678,7 +611,7 @@ static __devexit int adv7180_remove(struct i2c_client *client)
}
static const struct i2c_device_id adv7180_id[] = {
- {DRIVER_NAME, 0},
+ {KBUILD_MODNAME, 0},
{},
};
@@ -716,7 +649,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = DRIVER_NAME,
+ .name = KBUILD_MODNAME,
},
.probe = adv7180_probe,
.remove = __devexit_p(adv7180_remove),
diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c
new file mode 100644
index 000000000000..3dc6098c7267
--- /dev/null
+++ b/drivers/media/video/adv7393.c
@@ -0,0 +1,487 @@
+/*
+ * adv7393 - ADV7393 Video Encoder Driver
+ *
+ * The encoder hardware does not support SECAM.
+ *
+ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on ADV7343 driver,
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 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/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+
+#include <media/adv7393.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#include "adv7393_regs.h"
+
+MODULE_DESCRIPTION("ADV7393 video encoder driver");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+struct adv7393_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ u8 reg00;
+ u8 reg01;
+ u8 reg02;
+ u8 reg35;
+ u8 reg80;
+ u8 reg82;
+ u32 output;
+ v4l2_std_id std;
+};
+
+static inline struct adv7393_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7393_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd;
+}
+
+static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static const u8 adv7393_init_reg_val[] = {
+ ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT,
+ ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT,
+
+ ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT,
+ ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT,
+ ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT,
+ ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT,
+ ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT,
+ ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT,
+ ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT,
+
+ ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT,
+ ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT,
+ ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT,
+ ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT,
+ ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT,
+ ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT,
+ ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT,
+ ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT,
+
+ ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT,
+
+ ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT,
+ ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT,
+ ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT,
+};
+
+/*
+ * 2^32
+ * FSC(reg) = FSC (HZ) * --------
+ * 27000000
+ */
+static const struct adv7393_std_info stdinfo[] = {
+ {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443,
+ }, {
+ /* FSC(Hz) = 3,579,545.45 Hz */
+ SD_STD_NTSC, 569408542, V4L2_STD_NTSC,
+ }, {
+ /* FSC(Hz) = 3,575,611.00 Hz */
+ SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M,
+ }, {
+ /* FSC(Hz) = 3,582,056.00 Hz */
+ SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL,
+ },
+};
+
+static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7393_state *state = to_state(sd);
+ const struct adv7393_std_info *std_info;
+ int num_std;
+ u8 reg;
+ u32 val;
+ int err = 0;
+ int i;
+
+ num_std = ARRAY_SIZE(stdinfo);
+
+ for (i = 0; i < num_std; i++) {
+ if (stdinfo[i].stdid & std)
+ break;
+ }
+
+ if (i == num_std) {
+ v4l2_dbg(1, debug, sd,
+ "Invalid std or std is not supported: %llx\n",
+ (unsigned long long)std);
+ return -EINVAL;
+ }
+
+ std_info = &stdinfo[i];
+
+ /* Set the standard */
+ val = state->reg80 & ~SD_STD_MASK;
+ val |= std_info->standard_val3;
+ err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg80 = val;
+
+ /* Configure the input mode register */
+ val = state->reg01 & ~INPUT_MODE_MASK;
+ val |= SD_INPUT_MODE;
+ err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg01 = val;
+
+ /* Program the sub carrier frequency registers */
+ val = std_info->fsc_val;
+ for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) {
+ err = adv7393_write(sd, reg, val);
+ if (err < 0)
+ goto setstd_exit;
+ val >>= 8;
+ }
+
+ val = state->reg82;
+
+ /* Pedestal settings */
+ if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443))
+ val |= SD_PEDESTAL_EN;
+ else
+ val &= SD_PEDESTAL_DI;
+
+ err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg82 = val;
+
+setstd_exit:
+ if (err != 0)
+ v4l2_err(sd, "Error setting std, write failed\n");
+
+ return err;
+}
+
+static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type)
+{
+ struct adv7393_state *state = to_state(sd);
+ u8 val;
+ int err = 0;
+
+ if (output_type > ADV7393_SVIDEO_ID) {
+ v4l2_dbg(1, debug, sd,
+ "Invalid output type or output type not supported:%d\n",
+ output_type);
+ return -EINVAL;
+ }
+
+ /* Enable Appropriate DAC */
+ val = state->reg00 & 0x03;
+
+ if (output_type == ADV7393_COMPOSITE_ID)
+ val |= ADV7393_COMPOSITE_POWER_VALUE;
+ else if (output_type == ADV7393_COMPONENT_ID)
+ val |= ADV7393_COMPONENT_POWER_VALUE;
+ else
+ val |= ADV7393_SVIDEO_POWER_VALUE;
+
+ err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg00 = val;
+
+ /* Enable YUV output */
+ val = state->reg02 | YUV_OUTPUT_SELECT;
+ err = adv7393_write(sd, ADV7393_MODE_REG0, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg02 = val;
+
+ /* configure SD DAC Output 1 bit */
+ val = state->reg82;
+ if (output_type == ADV7393_COMPONENT_ID)
+ val &= SD_DAC_OUT1_DI;
+ else
+ val |= SD_DAC_OUT1_EN;
+ err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg82 = val;
+
+ /* configure ED/HD Color DAC Swap bit to zero */
+ val = state->reg35 & HD_DAC_SWAP_DI;
+ err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg35 = val;
+
+setoutput_exit:
+ if (err != 0)
+ v4l2_err(sd, "Error setting output, write failed\n");
+
+ return err;
+}
+
+static int adv7393_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7393_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std);
+ v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" :
+ ((state->output == 1) ? "Component" : "S-Video"));
+ return 0;
+}
+
+static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS,
+ ctrl->val & SD_BRIGHTNESS_VALUE_MASK);
+
+ case V4L2_CID_HUE:
+ return adv7393_write(sd, ADV7393_SD_HUE_ADJUST,
+ ctrl->val - ADV7393_HUE_MIN);
+
+ case V4L2_CID_GAIN:
+ return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL,
+ ctrl->val);
+ }
+ return -EINVAL;
+}
+
+static int adv7393_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0);
+}
+
+static const struct v4l2_ctrl_ops adv7393_ctrl_ops = {
+ .s_ctrl = adv7393_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7393_core_ops = {
+ .log_status = adv7393_log_status,
+ .g_chip_ident = adv7393_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7393_state *state = to_state(sd);
+ int err = 0;
+
+ if (state->std == std)
+ return 0;
+
+ err = adv7393_setstd(sd, std);
+ if (!err)
+ state->std = std;
+
+ return err;
+}
+
+static int adv7393_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7393_state *state = to_state(sd);
+ int err = 0;
+
+ if (state->output == output)
+ return 0;
+
+ err = adv7393_setoutput(sd, output);
+ if (!err)
+ state->output = output;
+
+ return err;
+}
+
+static const struct v4l2_subdev_video_ops adv7393_video_ops = {
+ .s_std_output = adv7393_s_std_output,
+ .s_routing = adv7393_s_routing,
+};
+
+static const struct v4l2_subdev_ops adv7393_ops = {
+ .core = &adv7393_core_ops,
+ .video = &adv7393_video_ops,
+};
+
+static int adv7393_initialize(struct v4l2_subdev *sd)
+{
+ struct adv7393_state *state = to_state(sd);
+ int err = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) {
+
+ err = adv7393_write(sd, adv7393_init_reg_val[i],
+ adv7393_init_reg_val[i+1]);
+ if (err) {
+ v4l2_err(sd, "Error initializing\n");
+ return err;
+ }
+ }
+
+ /* Configure for default video standard */
+ err = adv7393_setoutput(sd, state->output);
+ if (err < 0) {
+ v4l2_err(sd, "Error setting output during init\n");
+ return -EINVAL;
+ }
+
+ err = adv7393_setstd(sd, state->std);
+ if (err < 0) {
+ v4l2_err(sd, "Error setting std during init\n");
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static int adv7393_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7393_state *state;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT;
+ state->reg01 = 0x00;
+ state->reg02 = 0x20;
+ state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT;
+ state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT;
+ state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT;
+
+ state->output = ADV7393_COMPOSITE_ID;
+ state->std = V4L2_STD_NTSC;
+
+ v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops);
+
+ v4l2_ctrl_handler_init(&state->hdl, 3);
+ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN,
+ ADV7393_BRIGHTNESS_MAX, 1,
+ ADV7393_BRIGHTNESS_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+ V4L2_CID_HUE, ADV7393_HUE_MIN,
+ ADV7393_HUE_MAX, 1,
+ ADV7393_HUE_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
+ V4L2_CID_GAIN, ADV7393_GAIN_MIN,
+ ADV7393_GAIN_MAX, 1,
+ ADV7393_GAIN_DEF);
+ state->sd.ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ err = adv7393_initialize(&state->sd);
+ if (err) {
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ }
+ return err;
+}
+
+static int adv7393_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7393_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id adv7393_id[] = {
+ {"adv7393", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, adv7393_id);
+
+static struct i2c_driver adv7393_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7393",
+ },
+ .probe = adv7393_probe,
+ .remove = adv7393_remove,
+ .id_table = adv7393_id,
+};
+module_i2c_driver(adv7393_driver);
diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h
new file mode 100644
index 000000000000..78968330f0be
--- /dev/null
+++ b/drivers/media/video/adv7393_regs.h
@@ -0,0 +1,188 @@
+/*
+ * ADV7393 encoder related structure and register definitions
+ *
+ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
+ * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
+ *
+ * Based on ADV7343 driver,
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 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.
+ */
+
+#ifndef ADV7393_REGS_H
+#define ADV7393_REGS_H
+
+struct adv7393_std_info {
+ u32 standard_val3;
+ u32 fsc_val;
+ v4l2_std_id stdid;
+};
+
+/* Register offset macros */
+#define ADV7393_POWER_MODE_REG (0x00)
+#define ADV7393_MODE_SELECT_REG (0x01)
+#define ADV7393_MODE_REG0 (0x02)
+
+#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B)
+
+#define ADV7393_SOFT_RESET (0x17)
+
+#define ADV7393_HD_MODE_REG1 (0x30)
+#define ADV7393_HD_MODE_REG2 (0x31)
+#define ADV7393_HD_MODE_REG3 (0x32)
+#define ADV7393_HD_MODE_REG4 (0x33)
+#define ADV7393_HD_MODE_REG5 (0x34)
+#define ADV7393_HD_MODE_REG6 (0x35)
+
+#define ADV7393_HD_MODE_REG7 (0x39)
+
+#define ADV7393_SD_MODE_REG1 (0x80)
+#define ADV7393_SD_MODE_REG2 (0x82)
+#define ADV7393_SD_MODE_REG3 (0x83)
+#define ADV7393_SD_MODE_REG4 (0x84)
+#define ADV7393_SD_MODE_REG5 (0x86)
+#define ADV7393_SD_MODE_REG6 (0x87)
+#define ADV7393_SD_MODE_REG7 (0x88)
+#define ADV7393_SD_MODE_REG8 (0x89)
+
+#define ADV7393_SD_TIMING_REG0 (0x8A)
+
+#define ADV7393_FSC_REG0 (0x8C)
+#define ADV7393_FSC_REG1 (0x8D)
+#define ADV7393_FSC_REG2 (0x8E)
+#define ADV7393_FSC_REG3 (0x8F)
+
+#define ADV7393_SD_CGMS_WSS0 (0x99)
+
+#define ADV7393_SD_HUE_ADJUST (0xA0)
+#define ADV7393_SD_BRIGHTNESS_WSS (0xA1)
+
+/* Default values for the registers */
+#define ADV7393_POWER_MODE_REG_DEFAULT (0x10)
+#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default
+ 720p EAV/SAV code*/
+#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data
+ valid */
+#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */
+#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */
+#define ADV7393_HD_MODE_REG5_DEFAULT (0x08)
+#define ADV7393_HD_MODE_REG6_DEFAULT (0x00)
+#define ADV7393_HD_MODE_REG7_DEFAULT (0x00)
+#define ADV7393_SOFT_RESET_DEFAULT (0x02)
+#define ADV7393_COMPOSITE_POWER_VALUE (0x10)
+#define ADV7393_COMPONENT_POWER_VALUE (0x1C)
+#define ADV7393_SVIDEO_POWER_VALUE (0x0C)
+#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80)
+#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00)
+
+#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10)
+
+#define ADV7393_SD_MODE_REG1_DEFAULT (0x10)
+#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9)
+#define ADV7393_SD_MODE_REG3_DEFAULT (0x00)
+#define ADV7393_SD_MODE_REG4_DEFAULT (0x00)
+#define ADV7393_SD_MODE_REG5_DEFAULT (0x02)
+#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C)
+#define ADV7393_SD_MODE_REG7_DEFAULT (0x14)
+#define ADV7393_SD_MODE_REG8_DEFAULT (0x00)
+
+#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C)
+
+/* Bit masks for Mode Select Register */
+#define INPUT_MODE_MASK (0x70)
+#define SD_INPUT_MODE (0x00)
+#define HD_720P_INPUT_MODE (0x10)
+#define HD_1080I_INPUT_MODE (0x10)
+
+/* Bit masks for Mode Register 0 */
+#define TEST_PATTERN_BLACK_BAR_EN (0x04)
+#define YUV_OUTPUT_SELECT (0x20)
+#define RGB_OUTPUT_SELECT (0xDF)
+
+/* Bit masks for SD brightness/WSS */
+#define SD_BRIGHTNESS_VALUE_MASK (0x7F)
+#define SD_BLANK_WSS_DATA_MASK (0x80)
+
+/* Bit masks for soft reset register */
+#define SOFT_RESET (0x02)
+
+/* Bit masks for HD Mode Register 1 */
+#define OUTPUT_STD_MASK (0x03)
+#define OUTPUT_STD_SHIFT (0)
+#define OUTPUT_STD_EIA0_2 (0x00)
+#define OUTPUT_STD_EIA0_1 (0x01)
+#define OUTPUT_STD_FULL (0x02)
+#define EMBEDDED_SYNC (0x04)
+#define EXTERNAL_SYNC (0xFB)
+#define STD_MODE_MASK (0x1F)
+#define STD_MODE_SHIFT (3)
+#define STD_MODE_720P (0x05)
+#define STD_MODE_720P_25 (0x08)
+#define STD_MODE_720P_30 (0x07)
+#define STD_MODE_720P_50 (0x06)
+#define STD_MODE_1080I (0x0D)
+#define STD_MODE_1080I_25 (0x0E)
+#define STD_MODE_1080P_24 (0x11)
+#define STD_MODE_1080P_25 (0x10)
+#define STD_MODE_1080P_30 (0x0F)
+#define STD_MODE_525P (0x00)
+#define STD_MODE_625P (0x03)
+
+/* Bit masks for SD Mode Register 1 */
+#define SD_STD_MASK (0x03)
+#define SD_STD_NTSC (0x00)
+#define SD_STD_PAL_BDGHI (0x01)
+#define SD_STD_PAL_M (0x02)
+#define SD_STD_PAL_N (0x03)
+#define SD_LUMA_FLTR_MASK (0x07)
+#define SD_LUMA_FLTR_SHIFT (2)
+#define SD_CHROMA_FLTR_MASK (0x07)
+#define SD_CHROMA_FLTR_SHIFT (5)
+
+/* Bit masks for SD Mode Register 2 */
+#define SD_PRPB_SSAF_EN (0x01)
+#define SD_PRPB_SSAF_DI (0xFE)
+#define SD_DAC_OUT1_EN (0x02)
+#define SD_DAC_OUT1_DI (0xFD)
+#define SD_PEDESTAL_EN (0x08)
+#define SD_PEDESTAL_DI (0xF7)
+#define SD_SQUARE_PIXEL_EN (0x10)
+#define SD_SQUARE_PIXEL_DI (0xEF)
+#define SD_PIXEL_DATA_VALID (0x40)
+#define SD_ACTIVE_EDGE_EN (0x80)
+#define SD_ACTIVE_EDGE_DI (0x7F)
+
+/* Bit masks for HD Mode Register 6 */
+#define HD_PRPB_SYNC_EN (0x04)
+#define HD_PRPB_SYNC_DI (0xFB)
+#define HD_DAC_SWAP_EN (0x08)
+#define HD_DAC_SWAP_DI (0xF7)
+#define HD_GAMMA_CURVE_A (0xEF)
+#define HD_GAMMA_CURVE_B (0x10)
+#define HD_GAMMA_EN (0x20)
+#define HD_GAMMA_DI (0xDF)
+#define HD_ADPT_FLTR_MODEA (0xBF)
+#define HD_ADPT_FLTR_MODEB (0x40)
+#define HD_ADPT_FLTR_EN (0x80)
+#define HD_ADPT_FLTR_DI (0x7F)
+
+#define ADV7393_BRIGHTNESS_MAX (63)
+#define ADV7393_BRIGHTNESS_MIN (-64)
+#define ADV7393_BRIGHTNESS_DEF (0)
+#define ADV7393_HUE_MAX (127)
+#define ADV7393_HUE_MIN (-128)
+#define ADV7393_HUE_DEF (0)
+#define ADV7393_GAIN_MAX (64)
+#define ADV7393_GAIN_MIN (-64)
+#define ADV7393_GAIN_DEF (0)
+
+#endif
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 856ab962cd63..38952faaffda 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -345,7 +345,7 @@ static struct CARD {
{ 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" },
{ 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" },
{ 0x3116f200, BTTV_BOARD_TVT_TD3116, "Tongwei Video Technology TD-3116" },
-
+ { 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
{ 0, -1, NULL }
};
@@ -676,6 +676,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.has_remote = 1,
+ .has_radio = 1, /* not every card has radio */
},
[BTTV_BOARD_VOBIS_BOOSTAR] = {
.name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
@@ -2817,6 +2818,14 @@ struct tvcard bttv_tvcards[] = {
.pll = PLL_28,
.tuner_type = TUNER_ABSENT,
},
+ [BTTV_BOARD_APOSONIC_WDVR] = {
+ .name = "Aposonic W-DVR",
+ .video_inputs = 4,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_ABSENT,
+ },
+
};
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index ff7a589d8e0f..b58ff87db771 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -558,12 +558,6 @@ static const struct bttv_format formats[] = {
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .btformat = BT848_COLOR_FMT_YUY2,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
.name = "4:2:2, packed, UYVY",
.fourcc = V4L2_PIX_FMT_UYVY,
.btformat = BT848_COLOR_FMT_YUY2,
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index acfe2f3b92d9..79a11240a590 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -184,7 +184,7 @@
#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e
#define BTTV_BOARD_PV183 0x9f
#define BTTV_BOARD_TVT_TD3116 0xa0
-
+#define BTTV_BOARD_APOSONIC_WDVR 0xa1
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 55e92902a76c..a62a7b739991 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -932,7 +932,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
buf->sequence = cam->buffers[buf->index].seq;
buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
buf->length = cam->frame_size;
- buf->input = 0;
+ buf->reserved2 = 0;
buf->reserved = 0;
memset(&buf->timecode, 0, sizeof(buf->timecode));
diff --git a/drivers/media/video/cs8420.h b/drivers/media/video/cs8420.h
deleted file mode 100644
index 621c0c6678ea..000000000000
--- a/drivers/media/video/cs8420.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* cs8420.h - cs8420 initializations
- Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-#ifndef __CS8420_H__
-#define __CS8420_H__
-
-/* Initialization Sequence */
-
-static __u8 init8420[] = {
- 1, 0x01, 2, 0x02, 3, 0x00, 4, 0x46,
- 5, 0x24, 6, 0x84, 18, 0x18, 19, 0x13,
-};
-
-#define INIT8420LEN (sizeof(init8420)/2)
-
-static __u8 mode8420pro[] = { /* professional output mode */
- 32, 0xa1, 33, 0x00, 34, 0x00, 35, 0x00,
- 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
- 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
- 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
- 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
- 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
-};
-#define MODE8420LEN (sizeof(mode8420pro)/2)
-
-static __u8 mode8420con[] = { /* consumer output mode */
- 32, 0x20, 33, 0x00, 34, 0x00, 35, 0x48,
- 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
- 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
- 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
- 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
- 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
-};
-
-#endif
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 35fde4e931f5..e9912db3b496 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -1142,24 +1142,6 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio,
return 0;
}
-long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
-{
- struct video_device *vfd = video_devdata(filp);
- struct cx18_open_id *id = file2id(filp);
- struct cx18 *cx = id->cx;
- long res;
-
- mutex_lock(&cx->serialize_lock);
-
- if (cx18_debug & CX18_DBGFLG_IOCTL)
- vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
- res = video_ioctl2(filp, cmd, arg);
- vfd->debug = 0;
- mutex_unlock(&cx->serialize_lock);
- return res;
-}
-
static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
.vidioc_querycap = cx18_querycap,
.vidioc_s_audio = cx18_s_audio,
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
index dcb2559ad520..2f9dd591ee0f 100644
--- a/drivers/media/video/cx18/cx18-ioctl.h
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -29,5 +29,3 @@ void cx18_set_funcs(struct video_device *vdev);
int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
int cx18_s_input(struct file *file, void *fh, unsigned int inp);
-long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 4185bcb80ca3..9d598ab88615 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -40,8 +40,7 @@ static struct v4l2_file_operations cx18_v4l2_enc_fops = {
.owner = THIS_MODULE,
.read = cx18_v4l2_read,
.open = cx18_v4l2_open,
- /* FIXME change to video_ioctl2 if serialization lock can be removed */
- .unlocked_ioctl = cx18_v4l2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
.release = cx18_v4l2_close,
.poll = cx18_v4l2_enc_poll,
.mmap = cx18_v4l2_mmap,
@@ -376,6 +375,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
s->video_dev->fops = &cx18_v4l2_enc_fops;
s->video_dev->release = video_device_release;
s->video_dev->tvnorms = V4L2_STD_ALL;
+ s->video_dev->lock = &cx->serialize_lock;
set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
cx18_set_funcs(s->video_dev);
return 0;
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index b085a3c6dc04..447148eff958 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -89,7 +89,7 @@ void initGPIO(struct cx231xx *dev)
verve_read_byte(dev, 0x07, &val);
cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
- cx231xx_capture_start(dev, 1, 2);
+ cx231xx_capture_start(dev, 1, Vbi);
cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00);
cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF);
@@ -99,7 +99,7 @@ void uninitGPIO(struct cx231xx *dev)
{
u8 value[4] = { 0, 0, 0, 0 };
- cx231xx_capture_start(dev, 0, 2);
+ cx231xx_capture_start(dev, 0, Vbi);
verve_write_byte(dev, 0x07, 0x14);
cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
0x68, value, 4);
@@ -2516,29 +2516,29 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
if (dev->udev->speed == USB_SPEED_HIGH) {
switch (media_type) {
- case 81: /* audio */
+ case Audio:
cx231xx_info("%s: Audio enter HANC\n", __func__);
status =
cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
break;
- case 2: /* vbi */
+ case Vbi:
cx231xx_info("%s: set vanc registers\n", __func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
break;
- case 3: /* sliced cc */
+ case Sliced_cc:
cx231xx_info("%s: set hanc registers\n", __func__);
status =
cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
break;
- case 0: /* video */
+ case Raw_Video:
cx231xx_info("%s: set video registers\n", __func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
break;
- case 4: /* ts1 */
+ case TS1_serial_mode:
cx231xx_info("%s: set ts1 registers", __func__);
if (dev->board.has_417) {
@@ -2569,7 +2569,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
}
break;
- case 6: /* ts1 parallel mode */
+ case TS1_parallel_mode:
cx231xx_info("%s: set ts1 parallel mode registers\n",
__func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
@@ -2592,52 +2592,28 @@ int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
/* get EP for media type */
pcb_config = (struct pcb_config *)&dev->current_pcb_config;
- if (pcb_config->config_num == 1) {
+ if (pcb_config->config_num) {
switch (media_type) {
- case 0: /* Video */
+ case Raw_Video:
ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
break;
- case 1: /* Audio */
+ case Audio:
ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
break;
- case 2: /* Vbi */
+ case Vbi:
ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
break;
- case 3: /* Sliced_cc */
+ case Sliced_cc:
ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
break;
- case 4: /* ts1 */
- case 6: /* ts1 parallel mode */
+ case TS1_serial_mode:
+ case TS1_parallel_mode:
ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
break;
- case 5: /* ts2 */
+ case TS2:
ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
break;
}
-
- } else if (pcb_config->config_num > 1) {
- switch (media_type) {
- case 0: /* Video */
- ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
- break;
- case 1: /* Audio */
- ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
- break;
- case 2: /* Vbi */
- ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
- break;
- case 3: /* Sliced_cc */
- ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
- break;
- case 4: /* ts1 */
- case 6: /* ts1 parallel mode */
- ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
- break;
- case 5: /* ts2 */
- ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
- break;
- }
-
}
if (start) {
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 8ed460d692e0..02d4d36735d3 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -1023,7 +1023,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
int nr = 0, ifnum;
int i, isoc_pipe = 0;
char *speed;
- char descr[255] = "";
struct usb_interface_assoc_descriptor *assoc_desc;
udev = usb_get_dev(interface_to_usbdev(interface));
@@ -1098,20 +1097,10 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
speed = "unknown";
}
- if (udev->manufacturer)
- strlcpy(descr, udev->manufacturer, sizeof(descr));
-
- if (udev->product) {
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
- strlcat(descr, udev->product, sizeof(descr));
- }
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
-
- cx231xx_info("New device %s@ %s Mbps "
+ cx231xx_info("New device %s %s @ %s Mbps "
"(%04x:%04x) with %d interfaces\n",
- descr,
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
speed,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
index 925f3a04e53c..781feed406f7 100644
--- a/drivers/media/video/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
@@ -499,16 +499,12 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus)
BUG_ON(!dev->cx231xx_send_usb_command);
- memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
- memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
- memcpy(&bus->i2c_client, &cx231xx_client_template,
- sizeof(bus->i2c_client));
-
+ bus->i2c_adap = cx231xx_adap_template;
+ bus->i2c_client = cx231xx_client_template;
bus->i2c_adap.dev.parent = &dev->udev->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
- bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
index e17447554a0d..a89d020de948 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -26,7 +26,6 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
@@ -481,7 +480,6 @@ struct cx231xx_i2c {
/* i2c i/o */
struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index be1e21d8295c..4887314339cb 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -316,19 +316,13 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
- memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template,
- sizeof(bus->i2c_adap));
- memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template,
- sizeof(bus->i2c_algo));
- memcpy(&bus->i2c_client, &cx23885_i2c_client_template,
- sizeof(bus->i2c_client));
-
+ bus->i2c_adap = cx23885_i2c_adap_template;
+ bus->i2c_client = cx23885_i2c_client_template;
bus->i2c_adap.dev.parent = &dev->pci->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name,
sizeof(bus->i2c_adap.name));
- bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 13c37ec07ae7..5d560c747e09 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -21,7 +21,6 @@
#include <linux/pci.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
@@ -247,7 +246,6 @@ struct cx23885_i2c {
/* i2c i/o */
struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
index 6311180f430c..9844549764c9 100644
--- a/drivers/media/video/cx25821/cx25821-i2c.c
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
@@ -305,18 +305,12 @@ int cx25821_i2c_register(struct cx25821_i2c *bus)
dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
- memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
- sizeof(bus->i2c_adap));
- memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
- sizeof(bus->i2c_algo));
- memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
- sizeof(bus->i2c_client));
-
+ bus->i2c_adap = cx25821_i2c_adap_template;
+ bus->i2c_client = cx25821_i2c_client_template;
bus->i2c_adap.dev.parent = &dev->pci->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
- bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
index 313fb20a0b47..6a92e5c70c2a 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
@@ -499,7 +499,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
mutex_lock(&dev->lock);
/* no support */
- if (decoder < VDEC_A && decoder > VDEC_H) {
+ if (decoder < VDEC_A || decoder > VDEC_H) {
mutex_unlock(&dev->lock);
return;
}
diff --git a/drivers/media/video/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
index 029f2934a6d8..8a9c0c869412 100644
--- a/drivers/media/video/cx25821/cx25821.h
+++ b/drivers/media/video/cx25821/cx25821.h
@@ -26,7 +26,6 @@
#include <linux/pci.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/sched.h>
@@ -213,7 +212,6 @@ struct cx25821_i2c {
/* i2c i/o */
struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 04bf6627d362..dfac6e34859f 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -585,13 +585,10 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
- struct v4l2_control client_ctl;
int left = value->value.integer.value[0];
int right = value->value.integer.value[1];
int v, b;
- memset(&client_ctl, 0, sizeof(client_ctl));
-
/* Pass volume & balance onto any WM8775 */
if (left >= right) {
v = left << 10;
@@ -600,13 +597,8 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
v = right << 10;
b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
}
- client_ctl.value = v;
- client_ctl.id = V4L2_CID_AUDIO_VOLUME;
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
-
- client_ctl.value = b;
- client_ctl.id = V4L2_CID_AUDIO_BALANCE;
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
}
/* OK - TODO: test it */
@@ -687,14 +679,8 @@ static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
/* Pass mute onto any WM8775 */
if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
- ((1<<6) == bit)) {
- struct v4l2_control client_ctl;
-
- memset(&client_ctl, 0, sizeof(client_ctl));
- client_ctl.value = 0 != (vol & bit);
- client_ctl.id = V4L2_CID_AUDIO_MUTE;
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
- }
+ ((1<<6) == bit))
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
ret = 1;
}
spin_unlock_irq(&chip->reg_lock);
@@ -724,13 +710,10 @@ static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
{
snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
- struct v4l2_control client_ctl;
-
- memset(&client_ctl, 0, sizeof(client_ctl));
- client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
- call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
- value->value.integer.value[0] = client_ctl.value ? 1 : 0;
+ s32 val;
+ val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
+ value->value.integer.value[0] = val ? 1 : 0;
return 0;
}
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index ed7b2aa1ed83..843ffd9e533b 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -35,6 +35,7 @@
#include <linux/firmware.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
#include <media/cx2341x.h>
#include "cx88.h"
@@ -523,11 +524,10 @@ static void blackbird_codec_settings(struct cx8802_dev *dev)
blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
dev->height, dev->width);
- dev->params.width = dev->width;
- dev->params.height = dev->height;
- dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0;
-
- cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params);
+ dev->cxhdl.width = dev->width;
+ dev->cxhdl.height = dev->height;
+ cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
+ cx2341x_handler_setup(&dev->cxhdl);
}
static int blackbird_initialize_codec(struct cx8802_dev *dev)
@@ -618,6 +618,8 @@ static int blackbird_start_codec(struct file *file, void *priv)
/* initialize the video input */
blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ cx2341x_handler_set_busy(&dev->cxhdl, 1);
+
/* start capturing to the host interface */
blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
BLACKBIRD_MPEG_CAPTURE,
@@ -636,6 +638,8 @@ static int blackbird_stop_codec(struct cx8802_dev *dev)
BLACKBIRD_RAW_BITS_NONE
);
+ cx2341x_handler_set_busy(&dev->cxhdl, 0);
+
dev->mpeg_active = 0;
return 0;
}
@@ -685,58 +689,15 @@ static struct videobuf_queue_ops blackbird_qops = {
/* ------------------------------------------------------------------ */
-static const u32 *ctrl_classes[] = {
- cx88_user_ctrls,
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl)
-{
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- /* Standard V4L2 controls */
- if (cx8800_ctrl_query(dev->core, qctrl) == 0)
- return 0;
-
- /* MPEG V4L2 controls */
- if (cx2341x_ctrl_query(&dev->params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* IOCTL Handlers */
-
-static int vidioc_querymenu (struct file *file, void *priv,
- struct v4l2_querymenu *qmenu)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- blackbird_queryctrl(dev, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&dev->params, qmenu->id));
-}
-
-static int vidioc_querycap (struct file *file, void *priv,
+static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
struct cx88_core *core = dev->core;
strcpy(cap->driver, "cx88_blackbird");
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- if (UNSET != core->board.tuner_type)
- cap->capabilities |= V4L2_CAP_TUNER;
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cx88_querycap(file, core, cap);
return 0;
}
@@ -748,6 +709,7 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
strlcpy(f->description, "MPEG", sizeof(f->description));
f->pixelformat = V4L2_PIX_FMT_MPEG;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
@@ -759,12 +721,12 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */
- f->fmt.pix.colorspace = 0;
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
f->fmt.pix.field = fh->mpegq.field;
- dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
dev->width, dev->height, fh->mpegq.field );
return 0;
}
@@ -777,9 +739,9 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
- f->fmt.pix.colorspace = 0;
- dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
dev->width, dev->height, fh->mpegq.field );
return 0;
}
@@ -793,15 +755,15 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
- f->fmt.pix.colorspace = 0;
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
fh->mpegq.field = f->fmt.pix.field;
cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
f->fmt.pix.height, f->fmt.pix.width);
- dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
return 0;
}
@@ -834,60 +796,21 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
return videobuf_streamon(&fh->mpegq);
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct cx8802_fh *fh = priv;
- return videobuf_streamoff(&fh->mpegq);
-}
-
-static int vidioc_g_ext_ctrls (struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- return cx2341x_ext_ctrls(&dev->params, 0, f, VIDIOC_G_EXT_CTRLS);
-}
-
-static int vidioc_s_ext_ctrls (struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx2341x_mpeg_params p;
- int err;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
+ struct cx8802_dev *dev = fh->dev;
if (dev->mpeg_active)
blackbird_stop_codec(dev);
-
- p = dev->params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
- if (!err) {
- err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p);
- dev->params = p;
- }
- return err;
-}
-
-static int vidioc_try_ext_ctrls (struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx2341x_mpeg_params p;
- int err;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- p = dev->params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
-
- return err;
+ return videobuf_streamoff(&fh->mpegq);
}
static int vidioc_s_frequency (struct file *file, void *priv,
@@ -897,6 +820,10 @@ static int vidioc_s_frequency (struct file *file, void *priv,
struct cx8802_dev *dev = fh->dev;
struct cx88_core *core = dev->core;
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
if (dev->mpeg_active)
blackbird_stop_codec(dev);
@@ -914,29 +841,11 @@ static int vidioc_log_status (struct file *file, void *priv)
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", core->name);
- printk("%s/2: ============ START LOG STATUS ============\n",
- core->name);
call_all(core, core, log_status);
- cx2341x_log_status(&dev->params, name);
- printk("%s/2: ============= END LOG STATUS =============\n",
- core->name);
+ v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
return 0;
}
-static int vidioc_queryctrl (struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
-
- if (blackbird_queryctrl(dev, qctrl) == 0)
- return 0;
-
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (unlikely(qctrl->id == 0))
- return -EINVAL;
- return cx8800_ctrl_query(dev->core, qctrl);
-}
-
static int vidioc_enum_input (struct file *file, void *priv,
struct v4l2_input *i)
{
@@ -944,22 +853,6 @@ static int vidioc_enum_input (struct file *file, void *priv,
return cx88_enum_input (core,i);
}
-static int vidioc_g_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
- return
- cx88_get_control(core,ctl);
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
- return
- cx88_set_control(core,ctl);
-}
-
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
@@ -968,8 +861,9 @@ static int vidioc_g_frequency (struct file *file, void *priv,
if (unlikely(UNSET == core->board.tuner_type))
return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
- f->type = V4L2_TUNER_ANALOG_TV;
f->frequency = core->freq;
call_all(core, tuner, g_frequency, f);
@@ -990,6 +884,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
if (i >= 4)
return -EINVAL;
+ if (0 == INPUT(i).type)
+ return -EINVAL;
mutex_lock(&core->lock);
cx88_newstation(core);
@@ -1010,9 +906,9 @@ static int vidioc_g_tuner (struct file *file, void *priv,
return -EINVAL;
strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
t->capability = V4L2_TUNER_CAP_NORM;
t->rangehigh = 0xffffffffUL;
+ call_all(core, tuner, g_tuner, t);
cx88_get_stereo(core ,t);
reg = cx_read(MO_DEVICE_STATUS);
@@ -1034,6 +930,14 @@ static int vidioc_s_tuner (struct file *file, void *priv,
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ *tvnorm = core->tvnorm;
+ return 0;
+}
+
static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
{
struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
@@ -1087,6 +991,7 @@ static int mpeg_open(struct file *file)
mutex_unlock(&dev->core->lock);
return -ENOMEM;
}
+ v4l2_fh_init(&fh->fh, vdev);
file->private_data = fh;
fh->dev = dev;
@@ -1103,6 +1008,7 @@ static int mpeg_open(struct file *file)
dev->core->mpeg_users++;
mutex_unlock(&dev->core->lock);
+ v4l2_fh_add(&fh->fh);
return 0;
}
@@ -1123,6 +1029,8 @@ static int mpeg_release(struct file *file)
videobuf_mmap_free(&fh->mpegq);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
file->private_data = NULL;
kfree(fh);
@@ -1155,13 +1063,14 @@ mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
static unsigned int
mpeg_poll(struct file *file, struct poll_table_struct *wait)
{
+ unsigned long req_events = poll_requested_events(wait);
struct cx8802_fh *fh = file->private_data;
struct cx8802_dev *dev = fh->dev;
- if (!dev->mpeg_active)
+ if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
blackbird_start_codec(file, fh);
- return videobuf_poll_stream(file, &fh->mpegq, wait);
+ return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
}
static int
@@ -1180,11 +1089,10 @@ static const struct v4l2_file_operations mpeg_fops =
.read = mpeg_read,
.poll = mpeg_poll,
.mmap = mpeg_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
- .vidioc_querymenu = vidioc_querymenu,
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
@@ -1196,21 +1104,18 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
- .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_log_status = vidioc_log_status,
- .vidioc_queryctrl = vidioc_queryctrl,
.vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device cx8802_mpeg_template = {
@@ -1218,7 +1123,6 @@ static struct video_device cx8802_mpeg_template = {
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
.tvnorms = CX88_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
};
/* ------------------------------------------------------------------ */
@@ -1286,6 +1190,7 @@ static int blackbird_register_video(struct cx8802_dev *dev)
dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
&cx8802_mpeg_template,"mpeg");
+ dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
video_set_drvdata(dev->mpeg_dev, dev);
err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
if (err < 0) {
@@ -1318,17 +1223,20 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
goto fail_core;
dev->width = 720;
- dev->height = 576;
- cx2341x_fill_defaults(&dev->params);
- dev->params.port = CX2341X_PORT_STREAMING;
-
- cx8802_mpeg_template.current_norm = core->tvnorm;
-
if (core->tvnorm & V4L2_STD_525_60) {
dev->height = 480;
} else {
dev->height = 576;
}
+ dev->cxhdl.port = CX2341X_PORT_STREAMING;
+ dev->cxhdl.width = dev->width;
+ dev->cxhdl.height = dev->height;
+ dev->cxhdl.func = blackbird_mbox_func;
+ dev->cxhdl.priv = dev;
+ err = cx2341x_handler_init(&dev->cxhdl, 36);
+ if (err)
+ goto fail_core;
+ v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl);
/* blackbird stuff */
printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
@@ -1336,12 +1244,14 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
host_setup(dev->core);
blackbird_initialize_codec(dev);
- blackbird_register_video(dev);
/* initial device configuration: needed ? */
// init_controls(core);
cx88_set_tvnorm(core,core->tvnorm);
cx88_video_mux(core,0);
+ cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
+ cx2341x_handler_setup(&dev->cxhdl);
+ blackbird_register_video(dev);
return 0;
@@ -1351,8 +1261,12 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
static int cx8802_blackbird_remove(struct cx8802_driver *drv)
{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = core->dvbdev;
+
/* blackbird */
blackbird_unregister_video(drv->core->dvbdev);
+ v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
return 0;
}
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index cbd5d119a2c6..4e9d4f722960 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -3693,7 +3693,22 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
return NULL;
}
+ if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) {
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) {
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
if (0 != cx88_get_resources(core, pci)) {
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
v4l2_device_unregister(&core->v4l2_dev);
kfree(core);
return NULL;
@@ -3706,6 +3721,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
core->bmmio = (u8 __iomem *)core->lmmio;
if (core->lmmio == NULL) {
+ release_mem_region(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
kfree(core);
return NULL;
}
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index fbfdd8067937..e81c735f012a 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -1012,6 +1012,9 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
// tell i2c chips
call_all(core, core, s_std, norm);
+ /* The chroma_agc control should be inaccessible if the video format is SECAM */
+ v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
+
// done
return 0;
}
@@ -1030,10 +1033,10 @@ struct video_device *cx88_vdev_init(struct cx88_core *core,
return NULL;
*vfd = *template_;
vfd->v4l2_dev = &core->v4l2_dev;
- vfd->parent = &pci->dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
core->name, type, core->board.name);
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
return vfd;
}
@@ -1086,6 +1089,8 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
iounmap(core->lmmio);
cx88_devcount--;
mutex_unlock(&devlist);
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
v4l2_device_unregister(&core->v4l2_dev);
kfree(core);
}
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 921c56d115d6..f6fcc7e763ab 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -40,6 +40,7 @@
#include "cx88.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
#include <media/wm8775.h>
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
@@ -155,219 +156,147 @@ static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
/* ------------------------------------------------------------------- */
-static const struct v4l2_queryctrl no_ctl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
+struct cx88_ctrl {
+ /* control information */
+ u32 id;
+ s32 minimum;
+ s32 maximum;
+ u32 step;
+ s32 default_value;
+
+ /* control register information */
+ u32 off;
+ u32 reg;
+ u32 sreg;
+ u32 mask;
+ u32 shift;
};
-static const struct cx88_ctrl cx8800_ctls[] = {
+static const struct cx88_ctrl cx8800_vid_ctls[] = {
/* --- video --- */
{
- .v = {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0x00,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = MO_CONTR_BRIGHT,
- .mask = 0x00ff,
- .shift = 0,
+ .id = V4L2_CID_BRIGHTNESS,
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 128,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0x00ff,
+ .shift = 0,
},{
- .v = {
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = MO_CONTR_BRIGHT,
- .mask = 0xff00,
- .shift = 8,
+ .id = V4L2_CID_CONTRAST,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .off = 0,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0xff00,
+ .shift = 8,
},{
- .v = {
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = MO_HUE,
- .mask = 0x00ff,
- .shift = 0,
+ .id = V4L2_CID_HUE,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 128,
+ .reg = MO_HUE,
+ .mask = 0x00ff,
+ .shift = 0,
},{
/* strictly, this only describes only U saturation.
* V saturation is handled specially through code.
*/
- .v = {
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = MO_UV_SATURATION,
- .mask = 0x00ff,
- .shift = 0,
+ .id = V4L2_CID_SATURATION,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 0,
+ .reg = MO_UV_SATURATION,
+ .mask = 0x00ff,
+ .shift = 0,
}, {
- .v = {
- .id = V4L2_CID_SHARPNESS,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 4,
- .step = 1,
- .default_value = 0x0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
+ .id = V4L2_CID_SHARPNESS,
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 0x0,
+ .off = 0,
/* NOTE: the value is converted and written to both even
and odd registers in the code */
- .reg = MO_FILTER_ODD,
- .mask = 7 << 7,
- .shift = 7,
+ .reg = MO_FILTER_ODD,
+ .mask = 7 << 7,
+ .shift = 7,
}, {
- .v = {
- .id = V4L2_CID_CHROMA_AGC,
- .name = "Chroma AGC",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0x1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- .reg = MO_INPUT_FORMAT,
- .mask = 1 << 10,
- .shift = 10,
+ .id = V4L2_CID_CHROMA_AGC,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 10,
+ .shift = 10,
}, {
- .v = {
- .id = V4L2_CID_COLOR_KILLER,
- .name = "Color killer",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0x1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- .reg = MO_INPUT_FORMAT,
- .mask = 1 << 9,
- .shift = 9,
+ .id = V4L2_CID_COLOR_KILLER,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 9,
+ .shift = 9,
}, {
- .v = {
- .id = V4L2_CID_BAND_STOP_FILTER,
- .name = "Notch filter",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0x0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = MO_HTOTAL,
- .mask = 3 << 11,
- .shift = 11,
- }, {
- /* --- audio --- */
- .v = {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- .reg = AUD_VOL_CTL,
- .sreg = SHADOW_AUD_VOL_CTL,
- .mask = (1 << 6),
- .shift = 6,
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0x0,
+ .off = 0,
+ .reg = MO_HTOTAL,
+ .mask = 3 << 11,
+ .shift = 11,
+ }
+};
+
+static const struct cx88_ctrl cx8800_aud_ctls[] = {
+ {
+ /* --- audio --- */
+ .id = V4L2_CID_AUDIO_MUTE,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = (1 << 6),
+ .shift = 6,
},{
- .v = {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .reg = AUD_VOL_CTL,
- .sreg = SHADOW_AUD_VOL_CTL,
- .mask = 0x3f,
- .shift = 0,
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = 0x3f,
+ .shift = 0,
},{
- .v = {
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x40,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .reg = AUD_BAL_CTL,
- .sreg = SHADOW_AUD_BAL_CTL,
- .mask = 0x7f,
- .shift = 0,
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x40,
+ .reg = AUD_BAL_CTL,
+ .sreg = SHADOW_AUD_BAL_CTL,
+ .mask = 0x7f,
+ .shift = 0,
}
};
-enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) };
-
-/* Must be sorted from low to high control ID! */
-const u32 cx88_user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_BALANCE,
- V4L2_CID_AUDIO_MUTE,
- V4L2_CID_SHARPNESS,
- V4L2_CID_CHROMA_AGC,
- V4L2_CID_COLOR_KILLER,
- V4L2_CID_BAND_STOP_FILTER,
- 0
-};
-EXPORT_SYMBOL(cx88_user_ctrls);
-static const u32 * const ctrl_classes[] = {
- cx88_user_ctrls,
- NULL
+enum {
+ CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
+ CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
};
-int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl)
-{
- int i;
-
- if (qctrl->id < V4L2_CID_BASE ||
- qctrl->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- for (i = 0; i < CX8800_CTLS; i++)
- if (cx8800_ctls[i].v.id == qctrl->id)
- break;
- if (i == CX8800_CTLS) {
- *qctrl = no_ctl;
- return 0;
- }
- *qctrl = cx8800_ctls[i].v;
- /* Report chroma AGC as inactive when SECAM is selected */
- if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC &&
- core->tvnorm & V4L2_STD_SECAM)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
-
- return 0;
-}
-EXPORT_SYMBOL(cx8800_ctrl_query);
-
/* ------------------------------------------------------------------- */
/* resource management */
@@ -591,8 +520,9 @@ static int
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
struct cx8800_fh *fh = q->priv_data;
+ struct cx8800_dev *dev = fh->dev;
- *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ *size = dev->fmt->depth * dev->width * dev->height >> 3;
if (0 == *count)
*count = 32;
if (*size * *count > vid_limit * 1024 * 1024)
@@ -611,21 +541,21 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
int rc, init_buffer = 0;
- BUG_ON(NULL == fh->fmt);
- if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) ||
- fh->height < 32 || fh->height > norm_maxh(core->tvnorm))
+ BUG_ON(NULL == dev->fmt);
+ if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) ||
+ dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
return -EINVAL;
- buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
- if (buf->fmt != fh->fmt ||
- buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
+ if (buf->fmt != dev->fmt ||
+ buf->vb.width != dev->width ||
+ buf->vb.height != dev->height ||
buf->vb.field != field) {
- buf->fmt = fh->fmt;
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
+ buf->fmt = dev->fmt;
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
buf->vb.field = field;
init_buffer = 1;
}
@@ -675,7 +605,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
}
dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
buf, buf->vb.i,
- fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
(unsigned long)buf->risc.dma);
buf->vb.state = VIDEOBUF_PREPARED;
@@ -755,12 +685,15 @@ static const struct videobuf_queue_ops cx8800_video_qops = {
/* ------------------------------------------------------------------ */
-static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
+static struct videobuf_queue *get_queue(struct file *file)
{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = file->private_data;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
return &fh->vidq;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
return &fh->vbiq;
default:
BUG();
@@ -768,12 +701,14 @@ static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
}
}
-static int get_ressource(struct cx8800_fh *fh)
+static int get_resource(struct file *file)
{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ struct video_device *vdev = video_devdata(file);
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
return RESOURCE_VIDEO;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
return RESOURCE_VBI;
default:
BUG();
@@ -810,13 +745,9 @@ static int video_open(struct file *file)
if (unlikely(!fh))
return -ENOMEM;
+ v4l2_fh_init(&fh->fh, vdev);
file->private_data = fh;
fh->dev = dev;
- fh->radio = radio;
- fh->type = type;
- fh->width = 320;
- fh->height = 240;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
mutex_lock(&core->lock);
@@ -833,7 +764,7 @@ static int video_open(struct file *file)
sizeof(struct cx88_buffer),
fh, NULL);
- if (fh->radio) {
+ if (vdev->vfl_type == VFL_TYPE_RADIO) {
dprintk(1,"video_open: setting radio device\n");
cx_write(MO_GP3_IO, core->board.radio.gpio3);
cx_write(MO_GP0_IO, core->board.radio.gpio0);
@@ -859,6 +790,7 @@ static int video_open(struct file *file)
core->users++;
mutex_unlock(&core->lock);
+ v4l2_fh_add(&fh->fh);
return 0;
}
@@ -866,15 +798,16 @@ static int video_open(struct file *file)
static ssize_t
video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
+ struct video_device *vdev = video_devdata(file);
struct cx8800_fh *fh = file->private_data;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
if (res_locked(fh->dev,RESOURCE_VIDEO))
return -EBUSY;
return videobuf_read_one(&fh->vidq, data, count, ppos,
file->f_flags & O_NONBLOCK);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
if (!res_get(fh->dev,fh,RESOURCE_VBI))
return -EBUSY;
return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
@@ -888,16 +821,16 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
static unsigned int
video_poll(struct file *file, struct poll_table_struct *wait)
{
+ struct video_device *vdev = video_devdata(file);
struct cx8800_fh *fh = file->private_data;
struct cx88_buffer *buf;
- unsigned int rc = POLLERR;
+ unsigned int rc = v4l2_ctrl_poll(file, wait);
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return POLLERR;
- return videobuf_poll_stream(file, &fh->vbiq, wait);
+ return rc | POLLERR;
+ return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
}
-
mutex_lock(&fh->vidq.vb_lock);
if (res_check(fh,RESOURCE_VIDEO)) {
/* streaming capture */
@@ -913,9 +846,7 @@ video_poll(struct file *file, struct poll_table_struct *wait)
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == VIDEOBUF_DONE ||
buf->vb.state == VIDEOBUF_ERROR)
- rc = POLLIN|POLLRDNORM;
- else
- rc = 0;
+ rc |= POLLIN|POLLRDNORM;
done:
mutex_unlock(&fh->vidq.vb_lock);
return rc;
@@ -952,6 +883,8 @@ static int video_release(struct file *file)
videobuf_mmap_free(&fh->vbiq);
mutex_lock(&dev->core->lock);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
file->private_data = NULL;
kfree(fh);
@@ -966,156 +899,104 @@ static int video_release(struct file *file)
static int
video_mmap(struct file *file, struct vm_area_struct * vma)
{
- struct cx8800_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(get_queue(fh), vma);
+ return videobuf_mmap_mapper(get_queue(file), vma);
}
/* ------------------------------------------------------------------ */
/* VIDEO CTRL IOCTLS */
-int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl)
+static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
{
- const struct cx88_ctrl *c = NULL;
- u32 value;
- int i;
+ struct cx88_core *core =
+ container_of(ctrl->handler, struct cx88_core, video_hdl);
+ const struct cx88_ctrl *cc = ctrl->priv;
+ u32 value, mask;
- for (i = 0; i < CX8800_CTLS; i++)
- if (cx8800_ctls[i].v.id == ctl->id)
- c = &cx8800_ctls[i];
- if (unlikely(NULL == c))
- return -EINVAL;
+ mask = cc->mask;
+ switch (ctrl->id) {
+ case V4L2_CID_SATURATION:
+ /* special v_sat handling */
- value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
- switch (ctl->id) {
- case V4L2_CID_AUDIO_BALANCE:
- ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40)
- : (0x7f - (value & 0x7f));
- break;
- case V4L2_CID_AUDIO_VOLUME:
- ctl->value = 0x3f - (value & 0x3f);
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+
+ if (core->tvnorm & V4L2_STD_SECAM) {
+ /* For SECAM, both U and V sat should be equal */
+ value = value << 8 | value;
+ } else {
+ /* Keeps U Saturation proportional to V Sat */
+ value = (value * 0x5a) / 0x7f << 8 | value;
+ }
+ mask = 0xffff;
break;
case V4L2_CID_SHARPNESS:
- ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
- : 0);
+ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+ value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
+ /* needs to be set for both fields */
+ cx_andor(MO_FILTER_EVEN, mask, value);
+ break;
+ case V4L2_CID_CHROMA_AGC:
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
break;
default:
- ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
break;
}
- dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
- ctl->id, c->v.name, ctl->value, c->reg,
- value,c->mask, c->sreg ? " [shadowed]" : "");
+ dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+ mask, cc->sreg ? " [shadowed]" : "");
+ if (cc->sreg)
+ cx_sandor(cc->sreg, cc->reg, mask, value);
+ else
+ cx_andor(cc->reg, mask, value);
return 0;
}
-EXPORT_SYMBOL(cx88_get_control);
-int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
+static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
{
- const struct cx88_ctrl *c = NULL;
+ struct cx88_core *core =
+ container_of(ctrl->handler, struct cx88_core, audio_hdl);
+ const struct cx88_ctrl *cc = ctrl->priv;
u32 value,mask;
- int i;
-
- for (i = 0; i < CX8800_CTLS; i++) {
- if (cx8800_ctls[i].v.id == ctl->id) {
- c = &cx8800_ctls[i];
- }
- }
- if (unlikely(NULL == c))
- return -EINVAL;
-
- if (ctl->value < c->v.minimum)
- ctl->value = c->v.minimum;
- if (ctl->value > c->v.maximum)
- ctl->value = c->v.maximum;
/* Pass changes onto any WM8775 */
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
- struct v4l2_control client_ctl;
- memset(&client_ctl, 0, sizeof(client_ctl));
- client_ctl.id = ctl->id;
-
- switch (ctl->id) {
+ switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- client_ctl.value = ctl->value;
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val);
break;
case V4L2_CID_AUDIO_VOLUME:
- client_ctl.value = (ctl->value) ?
- (0x90 + ctl->value) << 8 : 0;
+ wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
+ (0x90 + ctrl->val) << 8 : 0);
break;
case V4L2_CID_AUDIO_BALANCE:
- client_ctl.value = ctl->value << 9;
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
break;
default:
- client_ctl.id = 0;
break;
}
- if (client_ctl.id)
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
}
- mask=c->mask;
- switch (ctl->id) {
+ mask = cc->mask;
+ switch (ctrl->id) {
case V4L2_CID_AUDIO_BALANCE:
- value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40);
+ value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
break;
case V4L2_CID_AUDIO_VOLUME:
- value = 0x3f - (ctl->value & 0x3f);
- break;
- case V4L2_CID_SATURATION:
- /* special v_sat handling */
-
- value = ((ctl->value - c->off) << c->shift) & c->mask;
-
- if (core->tvnorm & V4L2_STD_SECAM) {
- /* For SECAM, both U and V sat should be equal */
- value=value<<8|value;
- } else {
- /* Keeps U Saturation proportional to V Sat */
- value=(value*0x5a)/0x7f<<8|value;
- }
- mask=0xffff;
- break;
- case V4L2_CID_SHARPNESS:
- /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
- value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
- /* needs to be set for both fields */
- cx_andor(MO_FILTER_EVEN, mask, value);
- break;
- case V4L2_CID_CHROMA_AGC:
- /* Do not allow chroma AGC to be enabled for SECAM */
- value = ((ctl->value - c->off) << c->shift) & c->mask;
- if (core->tvnorm & V4L2_STD_SECAM && value)
- return -EINVAL;
+ value = 0x3f - (ctrl->val & 0x3f);
break;
default:
- value = ((ctl->value - c->off) << c->shift) & c->mask;
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
break;
}
dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
- ctl->id, c->v.name, ctl->value, c->reg, value,
- mask, c->sreg ? " [shadowed]" : "");
- if (c->sreg) {
- cx_sandor(c->sreg, c->reg, mask, value);
- } else {
- cx_andor(c->reg, mask, value);
- }
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+ mask, cc->sreg ? " [shadowed]" : "");
+ if (cc->sreg)
+ cx_sandor(cc->sreg, cc->reg, mask, value);
+ else
+ cx_andor(cc->reg, mask, value);
return 0;
}
-EXPORT_SYMBOL(cx88_set_control);
-
-static void init_controls(struct cx88_core *core)
-{
- struct v4l2_control ctrl;
- int i;
-
- for (i = 0; i < CX8800_CTLS; i++) {
- ctrl.id=cx8800_ctls[i].v.id;
- ctrl.value=cx8800_ctls[i].v.default_value;
-
- cx88_set_control(core, &ctrl);
- }
-}
/* ------------------------------------------------------------------ */
/* VIDEO IOCTLS */
@@ -1124,15 +1005,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
f->fmt.pix.field = fh->vidq.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
@@ -1184,33 +1067,54 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
int err = vidioc_try_fmt_vid_cap (file,priv,f);
if (0 != err)
return err;
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
+ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
return 0;
}
-static int vidioc_querycap (struct file *file, void *priv,
+void cx88_querycap(struct file *file, struct cx88_core *core,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ if (UNSET != core->board.tuner_type)
+ cap->device_caps |= V4L2_CAP_TUNER;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_RADIO:
+ cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
+ break;
+ case VFL_TYPE_GRABBER:
+ cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+ break;
+ }
+ cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
+ if (core->board.radio.type == CX88_RADIO)
+ cap->capabilities |= V4L2_CAP_RADIO;
+}
+EXPORT_SYMBOL(cx88_querycap);
+
+static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
struct cx88_core *core = dev->core;
strcpy(cap->driver, "cx8800");
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_VBI_CAPTURE;
- if (UNSET != core->board.tuner_type)
- cap->capabilities |= V4L2_CAP_TUNER;
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cx88_querycap(file, core, cap);
return 0;
}
@@ -1228,69 +1132,67 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
{
- struct cx8800_fh *fh = priv;
- return (videobuf_reqbufs(get_queue(fh), p));
+ return videobuf_reqbufs(get_queue(file), p);
}
static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct cx8800_fh *fh = priv;
- return (videobuf_querybuf(get_queue(fh), p));
+ return videobuf_querybuf(get_queue(file), p);
}
static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct cx8800_fh *fh = priv;
- return (videobuf_qbuf(get_queue(fh), p));
+ return videobuf_qbuf(get_queue(file), p);
}
static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct cx8800_fh *fh = priv;
- return (videobuf_dqbuf(get_queue(fh), p,
- file->f_flags & O_NONBLOCK));
+ return videobuf_dqbuf(get_queue(file), p,
+ file->f_flags & O_NONBLOCK);
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
+ struct video_device *vdev = video_devdata(file);
struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
- /* We should remember that this driver also supports teletext, */
- /* so we have to test if the v4l2_buf_type is VBI capture data. */
- if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)))
- return -EINVAL;
-
- if (unlikely(i != fh->type))
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
- if (unlikely(!res_get(dev,fh,get_ressource(fh))))
+ if (unlikely(!res_get(dev, fh, get_resource(file))))
return -EBUSY;
- return videobuf_streamon(get_queue(fh));
+ return videobuf_streamon(get_queue(file));
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
+ struct video_device *vdev = video_devdata(file);
struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
int err, res;
- if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
return -EINVAL;
- if (i != fh->type)
- return -EINVAL;
-
- res = get_ressource(fh);
- err = videobuf_streamoff(get_queue(fh));
+ res = get_resource(file);
+ err = videobuf_streamoff(get_queue(file));
if (err < 0)
return err;
res_free(dev,fh,res);
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ *tvnorm = core->tvnorm;
+ return 0;
+}
+
static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
{
struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
@@ -1327,8 +1229,8 @@ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
(CX88_VMUX_CABLE == INPUT(n).type)) {
i->type = V4L2_INPUT_TYPE_TUNER;
- i->std = CX88_NORMS;
}
+ i->std = CX88_NORMS;
return 0;
}
EXPORT_SYMBOL(cx88_enum_input);
@@ -1354,6 +1256,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
if (i >= 4)
return -EINVAL;
+ if (0 == INPUT(i).type)
+ return -EINVAL;
mutex_lock(&core->lock);
cx88_newstation(core);
@@ -1362,35 +1266,6 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
return 0;
}
-
-
-static int vidioc_queryctrl (struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (unlikely(qctrl->id == 0))
- return -EINVAL;
- return cx8800_ctrl_query(core, qctrl);
-}
-
-static int vidioc_g_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
- return
- cx88_get_control(core,ctl);
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
- return
- cx88_set_control(core,ctl);
-}
-
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *t)
{
@@ -1403,9 +1278,9 @@ static int vidioc_g_tuner (struct file *file, void *priv,
return -EINVAL;
strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
t->capability = V4L2_TUNER_CAP_NORM;
t->rangehigh = 0xffffffffUL;
+ call_all(core, tuner, g_tuner, t);
cx88_get_stereo(core ,t);
reg = cx_read(MO_DEVICE_STATUS);
@@ -1435,9 +1310,9 @@ static int vidioc_g_frequency (struct file *file, void *priv,
if (unlikely(UNSET == core->board.tuner_type))
return -EINVAL;
+ if (f->tuner)
+ return -EINVAL;
- /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = core->freq;
call_all(core, tuner, g_frequency, f);
@@ -1454,9 +1329,10 @@ int cx88_set_freq (struct cx88_core *core,
return -EINVAL;
mutex_lock(&core->lock);
- core->freq = f->frequency;
cx88_newstation(core);
call_all(core, tuner, s_frequency, f);
+ call_all(core, tuner, g_frequency, f);
+ core->freq = f->frequency;
/* When changing channels it is required to reset TVAUDIO */
msleep (10);
@@ -1474,13 +1350,17 @@ static int vidioc_s_frequency (struct file *file, void *priv,
struct cx8800_fh *fh = priv;
struct cx88_core *core = fh->dev->core;
- if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
- if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
- return -EINVAL;
+ return cx88_set_freq(core, f);
+}
- return
- cx88_set_freq (core,f);
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ if (!v4l2_chip_match_host(&chip->match))
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1513,19 +1393,6 @@ static int vidioc_s_register (struct file *file, void *fh,
/* RADIO ESPECIFIC IOCTLS */
/* ----------------------------------------------------------- */
-static int radio_querycap (struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
-
- strcpy(cap->driver, "cx8800");
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
static int radio_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *t)
{
@@ -1535,32 +1402,11 @@ static int radio_g_tuner (struct file *file, void *priv,
return -EINVAL;
strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
call_all(core, tuner, g_tuner, t);
return 0;
}
-static int radio_enum_input (struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
- strcpy(i->name,"Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- strcpy(a->name,"Radio");
- return 0;
-}
-
/* FIXME: Should add a standard for radio */
static int radio_s_tuner (struct file *file, void *priv,
@@ -1570,46 +1416,14 @@ static int radio_s_tuner (struct file *file, void *priv,
if (0 != t->index)
return -EINVAL;
+ if (t->audmode > V4L2_TUNER_MODE_STEREO)
+ t->audmode = V4L2_TUNER_MODE_STEREO;
call_all(core, tuner, s_tuner, t);
return 0;
}
-static int radio_s_audio (struct file *file, void *fh,
- struct v4l2_audio *a)
-{
- return 0;
-}
-
-static int radio_s_input (struct file *file, void *fh, unsigned int i)
-{
- return 0;
-}
-
-static int radio_queryctrl (struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- int i;
-
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE ||
- c->id == V4L2_CID_AUDIO_VOLUME ||
- c->id == V4L2_CID_AUDIO_BALANCE) {
- for (i = 0; i < CX8800_CTLS; i++) {
- if (cx8800_ctls[i].v.id == c->id)
- break;
- }
- if (i == CX8800_CTLS)
- return -EINVAL;
- *c = cx8800_ctls[i].v;
- } else
- *c = no_ctl;
- return 0;
-}
-
/* ----------------------------------------------------------- */
static void cx8800_vid_timeout(unsigned long data)
@@ -1752,63 +1566,89 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
};
-static struct video_device cx8800_vbi_template;
-
static const struct video_device cx8800_video_template = {
.name = "cx8800-video",
.fops = &video_fops,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX88_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_vbi_template = {
+ .name = "cx8800-vbi",
+ .fops = &video_fops,
+ .ioctl_ops = &vbi_ioctl_ops,
+ .tvnorms = CX88_NORMS,
};
static const struct v4l2_file_operations radio_fops =
{
.owner = THIS_MODULE,
.open = video_open,
+ .poll = v4l2_ctrl_poll,
.release = video_release,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
- .vidioc_querycap = radio_querycap,
+ .vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = radio_g_tuner,
- .vidioc_enum_input = radio_enum_input,
- .vidioc_g_audio = radio_g_audio,
.vidioc_s_tuner = radio_s_tuner,
- .vidioc_s_audio = radio_s_audio,
- .vidioc_s_input = radio_s_input,
- .vidioc_queryctrl = radio_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
@@ -1821,6 +1661,14 @@ static const struct video_device cx8800_radio_template = {
.ioctl_ops = &radio_ioctl_ops,
};
+static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
+ .s_ctrl = cx8800_s_vid_ctrl,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
+ .s_ctrl = cx8800_s_aud_ctrl,
+};
+
/* ----------------------------------------------------------- */
static void cx8800_unregister_video(struct cx8800_dev *dev)
@@ -1853,8 +1701,8 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
{
struct cx8800_dev *dev;
struct cx88_core *core;
-
int err;
+ int i;
dev = kzalloc(sizeof(*dev),GFP_KERNEL);
if (NULL == dev)
@@ -1888,14 +1736,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
goto fail_core;
}
- /* Initialize VBI template */
- memcpy( &cx8800_vbi_template, &cx8800_video_template,
- sizeof(cx8800_vbi_template) );
- strcpy(cx8800_vbi_template.name,"cx8800-vbi");
-
/* initialize driver struct */
spin_lock_init(&dev->slock);
- core->tvnorm = cx8800_video_template.current_norm;
+ core->tvnorm = V4L2_STD_NTSC_M;
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
@@ -1925,6 +1768,35 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+ for (i = 0; i < CX8800_AUD_CTLS; i++) {
+ const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
+ struct v4l2_ctrl *vc;
+
+ vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+ if (vc == NULL) {
+ err = core->audio_hdl.error;
+ goto fail_core;
+ }
+ vc->priv = (void *)cc;
+ }
+
+ for (i = 0; i < CX8800_VID_CTLS; i++) {
+ const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
+ struct v4l2_ctrl *vc;
+
+ vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+ if (vc == NULL) {
+ err = core->video_hdl.error;
+ goto fail_core;
+ }
+ vc->priv = (void *)cc;
+ if (vc->id == V4L2_CID_CHROMA_AGC)
+ core->chroma_agc = vc;
+ }
+ v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl);
+
/* load and configure helper modules */
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
@@ -1942,8 +1814,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
&wm8775_info, NULL);
- if (sd != NULL)
+ if (sd != NULL) {
+ core->sd_wm8775 = sd;
sd->grp_id = WM8775_GID;
+ }
}
if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
@@ -1971,16 +1845,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
/* Sets device info at pci_dev */
pci_set_drvdata(pci_dev, dev);
+ dev->width = 320;
+ dev->height = 240;
+ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
/* initial device configuration */
mutex_lock(&core->lock);
cx88_set_tvnorm(core, core->tvnorm);
- init_controls(core);
+ v4l2_ctrl_handler_setup(&core->video_hdl);
+ v4l2_ctrl_handler_setup(&core->audio_hdl);
cx88_video_mux(core, 0);
/* register v4l devices */
dev->video_dev = cx88_vdev_init(core,dev->pci,
&cx8800_video_template,"video");
video_set_drvdata(dev->video_dev, dev);
+ dev->video_dev->ctrl_handler = &core->video_hdl;
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[core->nr]);
if (err < 0) {
@@ -2007,6 +1887,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->radio_dev = cx88_vdev_init(core,dev->pci,
&cx8800_radio_template,"radio");
video_set_drvdata(dev->radio_dev, dev);
+ dev->radio_dev->ctrl_handler = &core->audio_hdl;
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[core->nr]);
if (err < 0) {
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index c9659def2a78..0cae0fd9e164 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -26,6 +26,7 @@
#include <linux/kdev_t.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/videobuf-dma-sg.h>
@@ -115,15 +116,6 @@ struct cx8800_fmt {
u32 cxformat;
};
-struct cx88_ctrl {
- struct v4l2_queryctrl v;
- u32 off;
- u32 reg;
- u32 sreg;
- u32 mask;
- u32 shift;
-};
-
/* ----------------------------------------------------------- */
/* SRAM memory management data (see cx88-core.c) */
@@ -359,6 +351,10 @@ struct cx88_core {
/* config info -- analog */
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler video_hdl;
+ struct v4l2_ctrl *chroma_agc;
+ struct v4l2_ctrl_handler audio_hdl;
+ struct v4l2_subdev *sd_wm8775;
struct i2c_client *i2c_rtc;
unsigned int boardnr;
struct cx88_board board;
@@ -409,8 +405,6 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
}
-#define WM8775_GID (1 << 0)
-
#define call_hw(core, grpid, o, f, args...) \
do { \
if (!core->i2c_rc) { \
@@ -424,6 +418,36 @@ static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+#define WM8775_GID (1 << 0)
+
+#define wm8775_s_ctrl(core, id, val) \
+ do { \
+ struct v4l2_ctrl *ctrl_ = \
+ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
+ if (ctrl_ && !core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ v4l2_ctrl_s_ctrl(ctrl_, val); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ } while (0)
+
+#define wm8775_g_ctrl(core, id) \
+ ({ \
+ struct v4l2_ctrl *ctrl_ = \
+ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
+ s32 val = 0; \
+ if (ctrl_ && !core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ val = v4l2_ctrl_g_ctrl(ctrl_); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ val; \
+ })
+
struct cx8800_dev;
struct cx8802_dev;
@@ -431,19 +455,11 @@ struct cx8802_dev;
/* function 0: video stuff */
struct cx8800_fh {
+ struct v4l2_fh fh;
struct cx8800_dev *dev;
- enum v4l2_buf_type type;
- int radio;
unsigned int resources;
- /* video overlay */
- struct v4l2_window win;
- struct v4l2_clip *clips;
- unsigned int nclips;
-
/* video capture */
- const struct cx8800_fmt *fmt;
- unsigned int width,height;
struct videobuf_queue vidq;
/* vbi capture */
@@ -468,6 +484,8 @@ struct cx8800_dev {
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
+ const struct cx8800_fmt *fmt;
+ unsigned int width, height;
/* capture queues */
struct cx88_dmaqueue vidq;
@@ -488,6 +506,7 @@ struct cx8800_dev {
/* function 2: mpeg stuff */
struct cx8802_fh {
+ struct v4l2_fh fh;
struct cx8802_dev *dev;
struct videobuf_queue mpegq;
};
@@ -552,7 +571,7 @@ struct cx8802_dev {
unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
/* mpeg params */
- struct cx2341x_mpeg_params params;
+ struct cx2341x_handler cxhdl;
#endif
#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
@@ -722,11 +741,8 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev);
/* ----------------------------------------------------------- */
/* cx88-video.c*/
-extern const u32 cx88_user_ctrls[];
-extern int cx8800_ctrl_query(struct cx88_core *core,
- struct v4l2_queryctrl *qctrl);
int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f);
-int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl);
-int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl);
int cx88_video_mux(struct cx88_core *core, unsigned int input);
+void cx88_querycap(struct file *file, struct cx88_core *core,
+ struct v4l2_capability *cap);
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
index 9337b5605c90..52c5ca68cb3d 100644
--- a/drivers/media/video/davinci/Kconfig
+++ b/drivers/media/video/davinci/Kconfig
@@ -1,30 +1,34 @@
-config DISPLAY_DAVINCI_DM646X_EVM
- tristate "DM646x EVM Video Display"
- depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
- select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_DISPLAY
+ tristate "DM646x/DA850/OMAPL138 EVM Video Display"
+ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ select VIDEOBUF2_DMA_CONTIG
select VIDEO_DAVINCI_VPIF
- select VIDEO_ADV7343
- select VIDEO_THS7303
+ select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO
help
- Support for DM6467 based display device.
+ Enables Davinci VPIF module used for display devices.
+ This module is common for following DM6467/DA850/OMAPL138
+ based display devices.
To compile this driver as a module, choose M here: the
module will be called vpif_display.
-config CAPTURE_DAVINCI_DM646X_EVM
- tristate "DM646x EVM Video Capture"
- depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
- select VIDEOBUF_DMA_CONTIG
+config VIDEO_DAVINCI_VPIF_CAPTURE
+ tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
+ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ select VIDEOBUF2_DMA_CONTIG
select VIDEO_DAVINCI_VPIF
help
- Support for DM6467 based capture device.
+ Enables Davinci VPIF module used for captur devices.
+ This module is common for following DM6467/DA850/OMAPL138
+ based capture devices.
To compile this driver as a module, choose M here: the
module will be called vpif_capture.
config VIDEO_DAVINCI_VPIF
tristate "DaVinci VPIF Driver"
- depends on DISPLAY_DAVINCI_DM646X_EVM
+ depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
help
Support for DaVinci VPIF Driver.
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile
index ae7dafb689ab..74ed92d09257 100644
--- a/drivers/media/video/davinci/Makefile
+++ b/drivers/media/video/davinci/Makefile
@@ -5,10 +5,10 @@
# VPIF
obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
-#DM646x EVM Display driver
-obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
-#DM646x EVM Capture driver
-obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
+#VPIF Display driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o
+#VPIF Capture driver
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o
# Capture: DM6446 and DM355
obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
index e106b72810a9..6fe7034bea7c 100644
--- a/drivers/media/video/davinci/vpbe_display.c
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -1083,7 +1083,7 @@ vpbe_display_s_dv_preset(struct file *file, void *priv,
}
/* Set the given standard in the encoder */
- if (NULL != vpbe_dev->ops.s_dv_preset)
+ if (!vpbe_dev->ops.s_dv_preset)
return -EINVAL;
ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
@@ -1517,6 +1517,8 @@ static int vpbe_display_g_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
struct v4l2_dbg_match *match = &reg->match;
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
if (match->type >= 2) {
v4l2_subdev_call(vpbe_dev->venc,
diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c
index af9680273ff9..b3637aff8fee 100644
--- a/drivers/media/video/davinci/vpif.c
+++ b/drivers/media/video/davinci/vpif.c
@@ -1,5 +1,5 @@
/*
- * vpif - DM646x Video Port Interface driver
+ * vpif - Video Port Interface driver
* VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
* that receiveing video byte stream and two channels(2, 3) for video output.
* The hardware supports SDTV, HDTV formats, raw data capture.
@@ -23,6 +23,8 @@
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <mach/hardware.h>
#include "vpif.h"
@@ -40,6 +42,7 @@ static struct resource *res;
spinlock_t vpif_lock;
void __iomem *vpif_base;
+struct clk *vpif_clk;
/**
* ch_params: video standard configuration parameters for vpif
@@ -346,7 +349,7 @@ static void config_vpif_params(struct vpif_params *vpifparams,
value = regr(reg);
/* Set data width */
- value &= ((~(unsigned int)(0x3)) <<
+ value &= ~(0x3u <<
VPIF_CH_DATA_WIDTH_BIT);
value |= ((vpifparams->params.data_sz) <<
VPIF_CH_DATA_WIDTH_BIT);
@@ -434,10 +437,19 @@ static int __init vpif_probe(struct platform_device *pdev)
goto fail;
}
+ vpif_clk = clk_get(&pdev->dev, "vpif");
+ if (IS_ERR(vpif_clk)) {
+ status = PTR_ERR(vpif_clk);
+ goto clk_fail;
+ }
+ clk_enable(vpif_clk);
+
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
return 0;
+clk_fail:
+ iounmap(vpif_base);
fail:
release_mem_region(res->start, res_len);
return status;
@@ -445,15 +457,44 @@ fail:
static int __devexit vpif_remove(struct platform_device *pdev)
{
+ if (vpif_clk) {
+ clk_disable(vpif_clk);
+ clk_put(vpif_clk);
+ }
+
iounmap(vpif_base);
release_mem_region(res->start, res_len);
return 0;
}
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+ clk_disable(vpif_clk);
+ return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+ clk_enable(vpif_clk);
+ return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
static struct platform_driver vpif_driver = {
.driver = {
.name = "vpif",
.owner = THIS_MODULE,
+ .pm = vpif_pm_ops,
},
.remove = __devexit_p(vpif_remove),
.probe = vpif_probe,
diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h
index 8bcac65f9294..c2ce4d97c279 100644
--- a/drivers/media/video/davinci/vpif.h
+++ b/drivers/media/video/davinci/vpif.h
@@ -211,6 +211,12 @@ static inline void vpif_clr_bit(u32 reg, u32 bit)
#define VPIF_CH3_INT_CTRL_SHIFT (6)
#define VPIF_CH_INT_CTRL_SHIFT (6)
+#define VPIF_CH2_CLIP_ANC_EN 14
+#define VPIF_CH2_CLIP_ACTIVE_EN 13
+
+#define VPIF_CH3_CLIP_ANC_EN 14
+#define VPIF_CH3_CLIP_ACTIVE_EN 13
+
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
@@ -515,6 +521,30 @@ static inline void channel3_raw_enable(int enable, u8 index)
vpif_clr_bit(VPIF_CH3_CTRL, mask);
}
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel2_clipping_enable(int enable)
+{
+ if (enable) {
+ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+ } else {
+ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+ }
+}
+
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel3_clipping_enable(int enable)
+{
+ if (enable) {
+ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+ } else {
+ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+ }
+}
+
/* inline function to set buffer addresses in case of Y/C non mux mode */
static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
@@ -569,6 +599,21 @@ static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
}
+static inline int vpif_intr_status(int channel)
+{
+ int status = 0;
+ int mask;
+
+ if (channel < 0 || channel > 3)
+ return 0;
+
+ mask = 1 << channel;
+ status = regr(VPIF_STATUS) & mask;
+ regw(status, VPIF_STATUS_CLR);
+
+ return status;
+}
+
#define VPIF_MAX_NAME (30)
/* This structure will store size parameters as per the mode selected by user */
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index 96046957bf21..266025e5d81d 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -80,108 +80,45 @@ static struct vpif_config_params config_params = {
/* global variables */
static struct vpif_device vpif_obj = { {NULL} };
static struct device *vpif_dev;
-
-/**
- * vpif_uservirt_to_phys : translate user/virtual address to phy address
- * @virtp: user/virtual address
- *
- * This inline function is used to convert user space virtual address to
- * physical address.
- */
-static inline u32 vpif_uservirt_to_phys(u32 virtp)
-{
- unsigned long physp = 0;
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
-
- vma = find_vma(mm, virtp);
-
- /* For kernel direct-mapped memory, take the easy way */
- if (virtp >= PAGE_OFFSET)
- physp = virt_to_phys((void *)virtp);
- else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
- /**
- * this will catch, kernel-allocated, mmaped-to-usermode
- * addresses
- */
- physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
- else {
- /* otherwise, use get_user_pages() for general userland pages */
- int res, nr_pages = 1;
- struct page *pages;
-
- down_read(&current->mm->mmap_sem);
-
- res = get_user_pages(current, current->mm,
- virtp, nr_pages, 1, 0, &pages, NULL);
- up_read(&current->mm->mmap_sem);
-
- if (res == nr_pages)
- physp = __pa(page_address(&pages[0]) +
- (virtp & ~PAGE_MASK));
- else {
- vpif_err("get_user_pages failed\n");
- return 0;
- }
- }
- return physp;
-}
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
/**
* buffer_prepare : callback function for buffer prepare
- * @q : buffer queue ptr
- * @vb: ptr to video buffer
- * @field: field info
+ * @vb: ptr to vb2_buffer
*
- * This is the callback function for buffer prepare when videobuf_qbuf()
+ * This is the callback function for buffer prepare when vb2_qbuf()
* function is called. The buffer is prepared and user space virtual address
* or user address is converted into physical address
*/
-static int vpif_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
{
/* Get the file handle object and channel object */
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
struct channel_obj *ch = fh->channel;
struct common_obj *common;
unsigned long addr;
-
vpif_dbg(2, debug, "vpif_buffer_prepare\n");
common = &ch->common[VPIF_VIDEO_INDEX];
- /* If buffer is not initialized, initialize it */
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = common->width;
- vb->height = common->height;
- vb->size = vb->width * vb->height;
- vb->field = field;
- }
- vb->state = VIDEOBUF_PREPARED;
- /**
- * if user pointer memory mechanism is used, get the physical
- * address of the buffer
- */
- if (V4L2_MEMORY_USERPTR == common->memory) {
- if (0 == vb->baddr) {
- vpif_dbg(1, debug, "buffer address is 0\n");
- return -EINVAL;
-
- }
- vb->boff = vpif_uservirt_to_phys(vb->baddr);
- if (!IS_ALIGNED(vb->boff, 8))
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED) {
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
goto exit;
- }
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- addr = vb->boff;
- if (q->streaming) {
- if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
- !IS_ALIGNED((addr + common->ybtm_off), 8) ||
- !IS_ALIGNED((addr + common->ctop_off), 8) ||
- !IS_ALIGNED((addr + common->cbtm_off), 8))
- goto exit;
+ if (q->streaming) {
+ if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+ !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+ !IS_ALIGNED((addr + common->ctop_off), 8) ||
+ !IS_ALIGNED((addr + common->cbtm_off), 8))
+ goto exit;
+ }
}
return 0;
exit:
@@ -190,49 +127,79 @@ exit:
}
/**
- * vpif_buffer_setup : Callback function for buffer setup.
- * @q: buffer queue ptr
- * @count: number of buffers
- * @size: size of the buffer
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
*
* This callback function is called when reqbuf() is called to adjust
* the buffer count and buffer size
*/
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
/* Get the file handle object and channel object */
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
+ unsigned long size;
common = &ch->common[VPIF_VIDEO_INDEX];
vpif_dbg(2, debug, "vpif_buffer_setup\n");
/* If memory type is not mmap, return */
- if (V4L2_MEMORY_MMAP != common->memory)
- return 0;
+ if (V4L2_MEMORY_MMAP == common->memory) {
+ /* Calculate the size of the buffer */
+ size = config_params.channel_bufsize[ch->channel_id];
+ /*
+ * Checking if the buffer size exceeds the available buffer
+ * ycmux_mode = 0 means 1 channel mode HD and
+ * ycmux_mode = 1 means 2 channels mode SD
+ */
+ if (ch->vpifparams.std_info.ycmux_mode == 0) {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ (config_params.video_limit[0]
+ + config_params.video_limit[1]))
+ (*nbuffers)--;
+ } else {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ config_params.video_limit[ch->channel_id])
+ (*nbuffers)--;
+ }
+
+ } else {
+ size = common->fmt.fmt.pix.sizeimage;
+ }
+
+ if (*nbuffers < config_params.min_numbuffers)
+ *nbuffers = config_params.min_numbuffers;
- /* Calculate the size of the buffer */
- *size = config_params.channel_bufsize[ch->channel_id];
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = common->alloc_ctx;
- if (*count < config_params.min_numbuffers)
- *count = config_params.min_numbuffers;
return 0;
}
/**
* vpif_buffer_queue : Callback function to add buffer to DMA queue
- * @q: ptr to videobuf_queue
- * @vb: ptr to videobuf_buffer
+ * @vb: ptr to vb2_buffer
*/
-static void vpif_buffer_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpif_buffer_queue(struct vb2_buffer *vb)
{
/* Get the file handle object and channel object */
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
struct channel_obj *ch = fh->channel;
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
struct common_obj *common;
common = &ch->common[VPIF_VIDEO_INDEX];
@@ -240,43 +207,189 @@ static void vpif_buffer_queue(struct videobuf_queue *q,
vpif_dbg(2, debug, "vpif_buffer_queue\n");
/* add the buffer to the DMA queue */
- list_add_tail(&vb->queue, &common->dma_queue);
- /* Change state of the buffer */
- vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->list, &common->dma_queue);
}
/**
- * vpif_buffer_release : Callback function to free buffer
- * @q: buffer queue ptr
- * @vb: ptr to video buffer
+ * vpif_buf_cleanup : Callback function to free buffer
+ * @vb: ptr to vb2_buffer
*
- * This function is called from the videobuf layer to free memory
+ * This function is called from the videobuf2 layer to free memory
* allocated to the buffers
*/
-static void vpif_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
{
/* Get the file handle object and channel object */
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
+ unsigned long flags;
common = &ch->common[VPIF_VIDEO_INDEX];
- videobuf_dma_contig_free(q, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
+ spin_lock_irqsave(&common->irqlock, flags);
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+
}
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = vpif_buffer_setup,
- .buf_prepare = vpif_buffer_prepare,
- .buf_queue = vpif_buffer_queue,
- .buf_release = vpif_buffer_release,
-};
+static void vpif_wait_prepare(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_unlock(&common->lock);
+}
+
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+}
+
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
{ {1, 1} };
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpif_capture_config *vpif_config_data =
+ vpif_dev->platform_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpif = &ch->vpifparams;
+ unsigned long addr = 0;
+ int ret;
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_dbg(1, debug, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&common->cur_frm->list);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((vpif->std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
+ (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
+ (!vpif->std_info.frm_fmt &&
+ (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_dbg(1, debug, "conflict in field format and std format\n");
+ return -EINVAL;
+ }
+
+ /* configure 1 or 2 channel mode */
+ ret = vpif_config_data->setup_input_channel_mode
+ (vpif->std_info.ycmux_mode);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set vpif channel mode\n");
+ return ret;
+ }
+
+ /* Call vpif_set_params function to set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set video params\n");
+ return ret;
+ }
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+
+ /**
+ * Set interrupt for both the fields in VPIF Register enable channel in
+ * VPIF register
+ */
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+ channel0_intr_assert();
+ channel0_intr_enable(1);
+ enable_channel0(1);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (common->started == 2)) {
+ channel1_intr_assert();
+ channel1_intr_enable(1);
+ enable_channel1(1);
+ }
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* release all active buffers */
+ while (!list_empty(&common->dma_queue)) {
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ list_del(&common->next_frm->list);
+ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpif_buffer_queue_setup,
+ .wait_prepare = vpif_wait_prepare,
+ .wait_finish = vpif_wait_finish,
+ .buf_init = vpif_buffer_init,
+ .buf_prepare = vpif_buffer_prepare,
+ .start_streaming = vpif_start_streaming,
+ .stop_streaming = vpif_stop_streaming,
+ .buf_cleanup = vpif_buf_cleanup,
+ .buf_queue = vpif_buffer_queue,
+};
+
/**
* vpif_process_buffer_complete: process a completed buffer
* @common: ptr to common channel object
@@ -287,9 +400,9 @@ static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
*/
static void vpif_process_buffer_complete(struct common_obj *common)
{
- do_gettimeofday(&common->cur_frm->ts);
- common->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&common->cur_frm->done);
+ do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
/* Make curFrm pointing to nextFrm */
common->cur_frm = common->next_frm;
}
@@ -307,14 +420,11 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
unsigned long addr = 0;
common->next_frm = list_entry(common->dma_queue.next,
- struct videobuf_buffer, queue);
+ struct vpif_cap_buffer, list);
/* Remove that buffer from the buffer queue */
- list_del(&common->next_frm->queue);
- common->next_frm->state = VIDEOBUF_ACTIVE;
- if (V4L2_MEMORY_USERPTR == common->memory)
- addr = common->next_frm->boff;
- else
- addr = videobuf_to_dma_contig(common->next_frm);
+ list_del(&common->next_frm->list);
+ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
/* Set top and bottom field addresses in VPIF registers */
common->set_addr(addr + common->ytop_off,
@@ -341,6 +451,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
int fid = -1, i;
channel_id = *(int *)(dev_id);
+ if (!vpif_intr_status(channel_id))
+ return IRQ_NONE;
+
ch = dev->dev[channel_id];
field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
@@ -485,10 +598,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
} else
vid_ch->buf_field = common->fmt.fmt.pix.field;
- if (V4L2_MEMORY_USERPTR == common->memory)
- sizeimage = common->fmt.fmt.pix.sizeimage;
- else
- sizeimage = config_params.channel_bufsize[ch->channel_id];
+ sizeimage = common->fmt.fmt.pix.sizeimage;
hpitch = common->fmt.fmt.pix.bytesperline;
vpitch = sizeimage / (hpitch * 2);
@@ -640,10 +750,7 @@ static int vpif_check_format(struct channel_obj *ch,
hpitch = vpif_params->std_info.width;
}
- if (V4L2_MEMORY_USERPTR == common->memory)
- sizeimage = pixfmt->sizeimage;
- else
- sizeimage = config_params.channel_bufsize[ch->channel_id];
+ sizeimage = pixfmt->sizeimage;
vpitch = sizeimage / (hpitch * 2);
@@ -703,7 +810,7 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
}
/**
- * vpfe_mmap : It is used to map kernel space buffers into user spaces
+ * vpif_mmap : It is used to map kernel space buffers into user spaces
* @filep: file pointer
* @vma: ptr to vm_area_struct
*/
@@ -716,7 +823,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
vpif_dbg(2, debug, "vpif_mmap\n");
- return videobuf_mmap_mapper(&common->buffer_queue, vma);
+ return vb2_mmap(&common->buffer_queue, vma);
}
/**
@@ -733,7 +840,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table * wait)
vpif_dbg(2, debug, "vpif_poll\n");
if (common->started)
- return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+ return vb2_poll(&common->buffer_queue, filep, wait);
return 0;
}
@@ -812,7 +919,7 @@ static int vpif_open(struct file *filep)
* vpif_release : function to clean up file close
* @filep: file pointer
*
- * This function deletes buffer queue, frees the buffers and the vpfe file
+ * This function deletes buffer queue, frees the buffers and the vpif file
* handle
*/
static int vpif_release(struct file *filep)
@@ -841,8 +948,8 @@ static int vpif_release(struct file *filep)
}
common->started = 0;
/* Free buffers allocated */
- videobuf_queue_cancel(&common->buffer_queue);
- videobuf_mmap_free(&common->buffer_queue);
+ vb2_queue_release(&common->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
}
/* Decrement channel usrs counter */
@@ -872,6 +979,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
struct channel_obj *ch = fh->channel;
struct common_obj *common;
u8 index = 0;
+ struct vb2_queue *q;
vpif_dbg(2, debug, "vpif_reqbufs\n");
@@ -887,7 +995,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
}
}
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type)
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
return -EINVAL;
index = VPIF_VIDEO_INDEX;
@@ -897,14 +1005,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
if (0 != common->io_usrs)
return -EBUSY;
- /* Initialize videobuf queue as per the buffer type */
- videobuf_queue_dma_contig_init(&common->buffer_queue,
- &video_qops, NULL,
- &common->irqlock,
- reqbuf->type,
- common->fmt.fmt.pix.field,
- sizeof(struct videobuf_buffer), fh,
- &common->lock);
+ /* Initialize videobuf2 queue as per the buffer type */
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (!common->alloc_ctx) {
+ vpif_err("Failed to get the context\n");
+ return -EINVAL;
+ }
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+
+ vb2_queue_init(q);
/* Set io allowed member of file handle to TRUE */
fh->io_allowed[index] = 1;
@@ -915,7 +1030,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
INIT_LIST_HEAD(&common->dma_queue);
/* Allocate buffers */
- return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+ return vb2_reqbufs(&common->buffer_queue, reqbuf);
}
/**
@@ -941,7 +1056,7 @@ static int vpif_querybuf(struct file *file, void *priv,
return -EINVAL;
}
- return videobuf_querybuf(&common->buffer_queue, buf);
+ return vb2_querybuf(&common->buffer_queue, buf);
}
/**
@@ -957,10 +1072,6 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct v4l2_buffer tbuf = *buf;
- struct videobuf_buffer *buf1;
- unsigned long addr = 0;
- unsigned long flags;
- int ret = 0;
vpif_dbg(2, debug, "vpif_qbuf\n");
@@ -970,76 +1081,11 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
}
if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh io not allowed \n");
+ vpif_err("fh io not allowed\n");
return -EACCES;
}
- if (!(list_empty(&common->dma_queue)) ||
- (common->cur_frm != common->next_frm) ||
- !common->started ||
- (common->started && (0 == ch->field_id)))
- return videobuf_qbuf(&common->buffer_queue, buf);
-
- /* bufferqueue is empty store buffer address in VPIF registers */
- mutex_lock(&common->buffer_queue.vb_lock);
- buf1 = common->buffer_queue.bufs[tbuf.index];
-
- if ((buf1->state == VIDEOBUF_QUEUED) ||
- (buf1->state == VIDEOBUF_ACTIVE)) {
- vpif_err("invalid state\n");
- goto qbuf_exit;
- }
-
- switch (buf1->memory) {
- case V4L2_MEMORY_MMAP:
- if (buf1->baddr == 0)
- goto qbuf_exit;
- break;
-
- case V4L2_MEMORY_USERPTR:
- if (tbuf.length < buf1->bsize)
- goto qbuf_exit;
-
- if ((VIDEOBUF_NEEDS_INIT != buf1->state)
- && (buf1->baddr != tbuf.m.userptr)) {
- vpif_buffer_release(&common->buffer_queue, buf1);
- buf1->baddr = tbuf.m.userptr;
- }
- break;
-
- default:
- goto qbuf_exit;
- }
-
- local_irq_save(flags);
- ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
- common->buffer_queue.field);
- if (ret < 0) {
- local_irq_restore(flags);
- goto qbuf_exit;
- }
-
- buf1->state = VIDEOBUF_ACTIVE;
-
- if (V4L2_MEMORY_USERPTR == common->memory)
- addr = buf1->boff;
- else
- addr = videobuf_to_dma_contig(buf1);
-
- common->next_frm = buf1;
- common->set_addr(addr + common->ytop_off,
- addr + common->ybtm_off,
- addr + common->ctop_off,
- addr + common->cbtm_off);
-
- local_irq_restore(flags);
- list_add_tail(&buf1->stream, &common->buffer_queue.stream);
- mutex_unlock(&common->buffer_queue.vb_lock);
- return 0;
-
-qbuf_exit:
- mutex_unlock(&common->buffer_queue.vb_lock);
- return -EINVAL;
+ return vb2_qbuf(&common->buffer_queue, buf);
}
/**
@@ -1056,8 +1102,8 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
vpif_dbg(2, debug, "vpif_dqbuf\n");
- return videobuf_dqbuf(&common->buffer_queue, buf,
- file->f_flags & O_NONBLOCK);
+ return vb2_dqbuf(&common->buffer_queue, buf,
+ (file->f_flags & O_NONBLOCK));
}
/**
@@ -1070,13 +1116,11 @@ static int vpif_streamon(struct file *file, void *priv,
enum v4l2_buf_type buftype)
{
- struct vpif_capture_config *config = vpif_dev->platform_data;
struct vpif_fh *fh = priv;
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
struct vpif_params *vpif;
- unsigned long addr = 0;
int ret = 0;
vpif_dbg(2, debug, "vpif_streamon\n");
@@ -1122,95 +1166,13 @@ static int vpif_streamon(struct file *file, void *priv,
return ret;
}
- /* Call videobuf_streamon to start streaming in videobuf */
- ret = videobuf_streamon(&common->buffer_queue);
+ /* Call vb2_streamon to start streaming in videobuf2 */
+ ret = vb2_streamon(&common->buffer_queue, buftype);
if (ret) {
- vpif_dbg(1, debug, "videobuf_streamon\n");
+ vpif_dbg(1, debug, "vb2_streamon\n");
return ret;
}
- /* If buffer queue is empty, return error */
- if (list_empty(&common->dma_queue)) {
- vpif_dbg(1, debug, "buffer queue is empty\n");
- ret = -EIO;
- goto exit;
- }
-
- /* Get the next frame from the buffer queue */
- common->cur_frm = list_entry(common->dma_queue.next,
- struct videobuf_buffer, queue);
- common->next_frm = common->cur_frm;
-
- /* Remove buffer from the buffer queue */
- list_del(&common->cur_frm->queue);
- /* Mark state of the current frame to active */
- common->cur_frm->state = VIDEOBUF_ACTIVE;
- /* Initialize field_id and started member */
- ch->field_id = 0;
- common->started = 1;
-
- if (V4L2_MEMORY_USERPTR == common->memory)
- addr = common->cur_frm->boff;
- else
- addr = videobuf_to_dma_contig(common->cur_frm);
-
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((vpif->std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
- (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
- (!vpif->std_info.frm_fmt &&
- (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_dbg(1, debug, "conflict in field format and std format\n");
- ret = -EINVAL;
- goto exit;
- }
-
- /* configure 1 or 2 channel mode */
- ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode);
-
- if (ret < 0) {
- vpif_dbg(1, debug, "can't set vpif channel mode\n");
- goto exit;
- }
-
- /* Call vpif_set_params function to set the parameters and addresses */
- ret = vpif_set_video_params(vpif, ch->channel_id);
-
- if (ret < 0) {
- vpif_dbg(1, debug, "can't set video params\n");
- goto exit;
- }
-
- common->started = ret;
- vpif_config_addr(ch, ret);
-
- common->set_addr(addr + common->ytop_off,
- addr + common->ybtm_off,
- addr + common->ctop_off,
- addr + common->cbtm_off);
-
- /**
- * Set interrupt for both the fields in VPIF Register enable channel in
- * VPIF register
- */
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
- channel0_intr_assert();
- channel0_intr_enable(1);
- enable_channel0(1);
- }
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (common->started == 2)) {
- channel1_intr_assert();
- channel1_intr_enable(1);
- enable_channel1(1);
- }
- channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
- return ret;
-
-exit:
- videobuf_streamoff(&common->buffer_queue);
return ret;
}
@@ -1265,7 +1227,7 @@ static int vpif_streamoff(struct file *file, void *priv,
if (ret && (ret != -ENOIOCTLCMD))
vpif_dbg(1, debug, "stream off failed in subdev\n");
- return videobuf_streamoff(&common->buffer_queue);
+ return vb2_streamoff(&common->buffer_queue, buftype);
}
/**
@@ -1679,7 +1641,7 @@ static int vpif_querycap(struct file *file, void *priv,
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
- strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
strlcpy(cap->card, config->card_name, sizeof(cap->card));
return 0;
@@ -2168,6 +2130,7 @@ static __init int vpif_probe(struct platform_device *pdev)
struct video_device *vfd;
struct resource *res;
int subdev_count;
+ size_t size;
vpif_dev = &pdev->dev;
@@ -2186,8 +2149,8 @@ static __init int vpif_probe(struct platform_device *pdev)
k = 0;
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
- "DM646x_Capture",
+ if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+ "VPIF_Capture",
(void *)(&vpif_obj.dev[k]->channel_id))) {
err = -EBUSY;
i--;
@@ -2216,12 +2179,29 @@ static __init int vpif_probe(struct platform_device *pdev)
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name),
- "DM646x_VPIFCapture_DRIVER_V%s",
+ "VPIF_Capture_DRIVER_V%s",
VPIF_CAPTURE_VERSION);
/* Set video_dev to the video device */
ch->video_dev = vfd;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ size = resource_size(res);
+ /* The resources are divided into two equal memory and when we
+ * have HD output we can add them together
+ */
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ /* only enabled if second resource exists */
+ config_params.video_limit[ch->channel_id] = 0;
+ if (size)
+ config_params.video_limit[ch->channel_id] =
+ size/2;
+ }
+ }
+
for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
ch->channel_id = j;
@@ -2275,8 +2255,7 @@ static __init int vpif_probe(struct platform_device *pdev)
vpif_obj.sd[i]->grp_id = 1 << i;
}
- v4l2_info(&vpif_obj.v4l2_dev,
- "DM646x VPIF capture driver initialized\n");
+ v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
return 0;
probe_subdev_out:
@@ -2333,26 +2312,70 @@ static int vpif_remove(struct platform_device *device)
return 0;
}
+#ifdef CONFIG_PM
/**
* vpif_suspend: vpif device suspend
- *
- * TODO: Add suspend code here
*/
-static int
-vpif_suspend(struct device *dev)
+static int vpif_suspend(struct device *dev)
{
- return -1;
+
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (ch->usrs && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ common->started == 2) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
}
-/**
+/*
* vpif_resume: vpif device suspend
- *
- * TODO: Add resume code here
*/
-static int
-vpif_resume(struct device *dev)
+static int vpif_resume(struct device *dev)
{
- return -1;
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (ch->usrs && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(1);
+ channel0_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ common->started == 2) {
+ enable_channel1(1);
+ channel1_intr_enable(1);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
}
static const struct dev_pm_ops vpif_dev_pm_ops = {
@@ -2360,11 +2383,16 @@ static const struct dev_pm_ops vpif_dev_pm_ops = {
.resume = vpif_resume,
};
+#define vpif_pm_ops (&vpif_dev_pm_ops)
+#else
+#define vpif_pm_ops NULL
+#endif
+
static __refdata struct platform_driver vpif_driver = {
.driver = {
.name = "vpif_capture",
.owner = THIS_MODULE,
- .pm = &vpif_dev_pm_ops,
+ .pm = vpif_pm_ops,
},
.probe = vpif_probe,
.remove = vpif_remove,
diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h
index a693d4ebda55..3511510f43ee 100644
--- a/drivers/media/video/davinci/vpif_capture.h
+++ b/drivers/media/video/davinci/vpif_capture.h
@@ -26,7 +26,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/davinci/vpif_types.h>
#include "vpif.h"
@@ -60,11 +60,16 @@ struct video_obj {
u32 input_idx;
};
+struct vpif_cap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
struct common_obj {
/* Pointer pointing to current v4l2_buffer */
- struct videobuf_buffer *cur_frm;
+ struct vpif_cap_buffer *cur_frm;
/* Pointer pointing to current v4l2_buffer */
- struct videobuf_buffer *next_frm;
+ struct vpif_cap_buffer *next_frm;
/*
* This field keeps track of type of buffer exchange mechanism
* user has selected
@@ -73,7 +78,9 @@ struct common_obj {
/* Used to store pixel format */
struct v4l2_format fmt;
/* Buffer queue used in video-buf */
- struct videobuf_queue buffer_queue;
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
/* Queue of filled frames */
struct list_head dma_queue;
/* Used in video-buf */
@@ -151,6 +158,7 @@ struct vpif_config_params {
u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
+ u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
u8 max_device_type;
};
/* Struct which keeps track of the line numbers for the sliced vbi service */
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index e6488ee7db18..e129c98921ad 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -46,7 +46,7 @@ MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VPIF_DISPLAY_VERSION);
-#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
+#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
#define vpif_dbg(level, debug, fmt, arg...) \
@@ -82,89 +82,38 @@ static struct vpif_config_params config_params = {
static struct vpif_device vpif_obj = { {NULL} };
static struct device *vpif_dev;
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
/*
- * vpif_uservirt_to_phys: This function is used to convert user
- * space virtual address to physical address.
- */
-static u32 vpif_uservirt_to_phys(u32 virtp)
-{
- struct mm_struct *mm = current->mm;
- unsigned long physp = 0;
- struct vm_area_struct *vma;
-
- vma = find_vma(mm, virtp);
-
- /* For kernel direct-mapped memory, take the easy way */
- if (virtp >= PAGE_OFFSET) {
- physp = virt_to_phys((void *)virtp);
- } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
- /* this will catch, kernel-allocated, mmaped-to-usermode addr */
- physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
- } else {
- /* otherwise, use get_user_pages() for general userland pages */
- int res, nr_pages = 1;
- struct page *pages;
- down_read(&current->mm->mmap_sem);
-
- res = get_user_pages(current, current->mm,
- virtp, nr_pages, 1, 0, &pages, NULL);
- up_read(&current->mm->mmap_sem);
-
- if (res == nr_pages) {
- physp = __pa(page_address(&pages[0]) +
- (virtp & ~PAGE_MASK));
- } else {
- vpif_err("get_user_pages failed\n");
- return 0;
- }
- }
-
- return physp;
-}
-
-/*
- * buffer_prepare: This is the callback function called from videobuf_qbuf()
+ * buffer_prepare: This is the callback function called from vb2_qbuf()
* function the buffer is prepared and user space virtual address is converted
* into physical address
*/
-static int vpif_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
{
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
struct common_obj *common;
unsigned long addr;
common = &fh->channel->common[VPIF_VIDEO_INDEX];
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = common->width;
- vb->height = common->height;
- vb->size = vb->width * vb->height;
- vb->field = field;
- }
- vb->state = VIDEOBUF_PREPARED;
-
- /* if user pointer memory mechanism is used, get the physical
- * address of the buffer */
- if (V4L2_MEMORY_USERPTR == common->memory) {
- if (!vb->baddr) {
- vpif_err("buffer_address is 0\n");
- return -EINVAL;
- }
-
- vb->boff = vpif_uservirt_to_phys(vb->baddr);
- if (!ISALIGNED(vb->boff))
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED) {
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
goto buf_align_exit;
- }
- addr = vb->boff;
- if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
- if (!ISALIGNED(addr + common->ytop_off) ||
- !ISALIGNED(addr + common->ybtm_off) ||
- !ISALIGNED(addr + common->ctop_off) ||
- !ISALIGNED(addr + common->cbtm_off))
- goto buf_align_exit;
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (q->streaming &&
+ (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
+ if (!ISALIGNED(addr + common->ytop_off) ||
+ !ISALIGNED(addr + common->ybtm_off) ||
+ !ISALIGNED(addr + common->ctop_off) ||
+ !ISALIGNED(addr + common->cbtm_off))
+ goto buf_align_exit;
+ }
}
return 0;
@@ -174,86 +123,255 @@ buf_align_exit:
}
/*
- * vpif_buffer_setup: This function allocates memory for the buffers
+ * vpif_buffer_queue_setup: This function allocates memory for the buffers
*/
-static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ unsigned long size;
+
+ if (V4L2_MEMORY_MMAP == common->memory) {
+ size = config_params.channel_bufsize[ch->channel_id];
+ /*
+ * Checking if the buffer size exceeds the available buffer
+ * ycmux_mode = 0 means 1 channel mode HD and
+ * ycmux_mode = 1 means 2 channels mode SD
+ */
+ if (ch->vpifparams.std_info.ycmux_mode == 0) {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ (config_params.video_limit[0]
+ + config_params.video_limit[1]))
+ (*nbuffers)--;
+ } else {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ config_params.video_limit[ch->channel_id])
+ (*nbuffers)--;
+ }
+ } else {
+ size = common->fmt.fmt.pix.sizeimage;
+ }
- if (V4L2_MEMORY_MMAP != common->memory)
- return 0;
-
- *size = config_params.channel_bufsize[ch->channel_id];
- if (*count < config_params.min_numbuffers)
- *count = config_params.min_numbuffers;
+ if (*nbuffers < config_params.min_numbuffers)
+ *nbuffers = config_params.min_numbuffers;
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = common->alloc_ctx;
return 0;
}
/*
* vpif_buffer_queue: This function adds the buffer to DMA queue
*/
-static void vpif_buffer_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpif_buffer_queue(struct vb2_buffer *vb)
{
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+ struct channel_obj *ch = fh->channel;
struct common_obj *common;
- common = &fh->channel->common[VPIF_VIDEO_INDEX];
+ common = &ch->common[VPIF_VIDEO_INDEX];
/* add the buffer to the DMA queue */
- list_add_tail(&vb->queue, &common->dma_queue);
- vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->list, &common->dma_queue);
}
/*
- * vpif_buffer_release: This function is called from the videobuf layer to
+ * vpif_buf_cleanup: This function is called from the videobuf2 layer to
* free memory allocated to the buffers
*/
-static void vpif_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long flags;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ spin_lock_irqsave(&common->irqlock, flags);
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+}
+
+static void vpif_wait_prepare(struct vb2_queue *vq)
{
- struct vpif_fh *fh = q->priv_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
- unsigned int buf_size = 0;
common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_unlock(&common->lock);
+}
- videobuf_dma_contig_free(q, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
- if (V4L2_MEMORY_MMAP != common->memory)
- return;
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+}
- buf_size = config_params.channel_bufsize[ch->channel_id];
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
}
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = vpif_buffer_setup,
- .buf_prepare = vpif_buffer_prepare,
- .buf_queue = vpif_buffer_queue,
- .buf_release = vpif_buffer_release,
-};
static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpif_display_config *vpif_config_data =
+ vpif_dev->platform_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpif = &ch->vpifparams;
+ unsigned long addr = 0;
+ int ret;
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_err("buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->next_frm = common->cur_frm =
+ list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+
+ list_del(&common->cur_frm->list);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((ch->vpifparams.std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
+ && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
+ || (!ch->vpifparams.std_info.frm_fmt
+ && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_err("conflict in field format and std format\n");
+ return -EINVAL;
+ }
+
+ /* clock settings */
+ ret =
+ vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
+ ch->vpifparams.std_info.hd_sd);
+ if (ret < 0) {
+ vpif_err("can't set clock\n");
+ return ret;
+ }
+
+ /* set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id + 2);
+ if (ret < 0)
+ return ret;
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+ common->set_addr((addr + common->ytop_off),
+ (addr + common->ybtm_off),
+ (addr + common->ctop_off),
+ (addr + common->cbtm_off));
+
+ /* Set interrupt for both the fields in VPIF
+ Register enable channel in VPIF register */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ channel2_intr_assert();
+ channel2_intr_enable(1);
+ enable_channel2(1);
+ if (vpif_config_data->ch2_clip_en)
+ channel2_clipping_enable(1);
+ }
+
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
+ || (common->started == 2)) {
+ channel3_intr_assert();
+ channel3_intr_enable(1);
+ enable_channel3(1);
+ if (vpif_config_data->ch3_clip_en)
+ channel3_clipping_enable(1);
+ }
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* release all active buffers */
+ while (!list_empty(&common->dma_queue)) {
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+ list_del(&common->next_frm->list);
+ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpif_buffer_queue_setup,
+ .wait_prepare = vpif_wait_prepare,
+ .wait_finish = vpif_wait_finish,
+ .buf_init = vpif_buffer_init,
+ .buf_prepare = vpif_buffer_prepare,
+ .start_streaming = vpif_start_streaming,
+ .stop_streaming = vpif_stop_streaming,
+ .buf_cleanup = vpif_buf_cleanup,
+ .buf_queue = vpif_buffer_queue,
+};
+
static void process_progressive_mode(struct common_obj *common)
{
unsigned long addr = 0;
/* Get the next buffer from buffer queue */
common->next_frm = list_entry(common->dma_queue.next,
- struct videobuf_buffer, queue);
+ struct vpif_disp_buffer, list);
/* Remove that buffer from the buffer queue */
- list_del(&common->next_frm->queue);
+ list_del(&common->next_frm->list);
/* Mark status of the buffer as active */
- common->next_frm->state = VIDEOBUF_ACTIVE;
+ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
/* Set top and bottom field addrs in VPIF registers */
- addr = videobuf_to_dma_contig(common->next_frm);
+ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
common->set_addr(addr + common->ytop_off,
addr + common->ybtm_off,
addr + common->ctop_off,
@@ -271,11 +389,10 @@ static void process_interlaced_mode(int fid, struct common_obj *common)
/* one frame is displayed If next frame is
* available, release cur_frm and move on */
/* Copy frame display time */
- do_gettimeofday(&common->cur_frm->ts);
+ do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
/* Change status of the cur_frm */
- common->cur_frm->state = VIDEOBUF_DONE;
- /* unlock semaphore on cur_frm */
- wake_up_interruptible(&common->cur_frm->done);
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
common->cur_frm = common->next_frm;
@@ -307,6 +424,9 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
int channel_id = 0;
channel_id = *(int *)(dev_id);
+ if (!vpif_intr_status(channel_id + 2))
+ return IRQ_NONE;
+
ch = dev->dev[channel_id];
field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
for (i = 0; i < VPIF_NUMOBJECTS; i++) {
@@ -323,9 +443,10 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
if (!channel_first_int[i][channel_id]) {
/* Mark status of the cur_frm to
* done and unlock semaphore on it */
- do_gettimeofday(&common->cur_frm->ts);
- common->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&common->cur_frm->done);
+ do_gettimeofday(&common->cur_frm->vb.
+ v4l2_buf.timestamp);
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
common->cur_frm = common->next_frm;
}
@@ -443,10 +564,7 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
vid_ch->buf_field = common->fmt.fmt.pix.field;
}
- if (V4L2_MEMORY_USERPTR == common->memory)
- sizeimage = common->fmt.fmt.pix.sizeimage;
- else
- sizeimage = config_params.channel_bufsize[ch->channel_id];
+ sizeimage = common->fmt.fmt.pix.sizeimage;
hpitch = common->fmt.fmt.pix.bytesperline;
vpitch = sizeimage / (hpitch * 2);
@@ -523,10 +641,7 @@ static int vpif_check_format(struct channel_obj *ch,
if (pixfmt->bytesperline <= 0)
goto invalid_pitch_exit;
- if (V4L2_MEMORY_USERPTR == common->memory)
- sizeimage = pixfmt->sizeimage;
- else
- sizeimage = config_params.channel_bufsize[ch->channel_id];
+ sizeimage = pixfmt->sizeimage;
if (vpif_update_resolution(ch))
return -EINVAL;
@@ -583,7 +698,7 @@ static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
vpif_dbg(2, debug, "vpif_mmap\n");
- return videobuf_mmap_mapper(&common->buffer_queue, vma);
+ return vb2_mmap(&common->buffer_queue, vma);
}
/*
@@ -596,7 +711,7 @@ static unsigned int vpif_poll(struct file *filep, poll_table *wait)
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
if (common->started)
- return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+ return vb2_poll(&common->buffer_queue, filep, wait);
return 0;
}
@@ -665,9 +780,11 @@ static int vpif_release(struct file *filep)
channel3_intr_enable(0);
}
common->started = 0;
+
/* Free buffers allocated */
- videobuf_queue_cancel(&common->buffer_queue);
- videobuf_mmap_free(&common->buffer_queue);
+ vb2_queue_release(&common->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+
common->numbuffers =
config_params.numbuffers[ch->channel_id];
}
@@ -806,6 +923,7 @@ static int vpif_reqbufs(struct file *file, void *priv,
struct channel_obj *ch = fh->channel;
struct common_obj *common;
enum v4l2_field field;
+ struct vb2_queue *q;
u8 index = 0;
/* This file handle has not initialized the channel,
@@ -825,9 +943,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
common = &ch->common[index];
- if (common->fmt.type != reqbuf->type)
+ if (common->fmt.type != reqbuf->type || !vpif_dev)
return -EINVAL;
-
if (0 != common->io_usrs)
return -EBUSY;
@@ -839,14 +956,21 @@ static int vpif_reqbufs(struct file *file, void *priv,
} else {
field = V4L2_VBI_INTERLACED;
}
+ /* Initialize videobuf2 queue as per the buffer type */
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (!common->alloc_ctx) {
+ vpif_err("Failed to get the context\n");
+ return -EINVAL;
+ }
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_disp_buffer);
- /* Initialize videobuf queue as per the buffer type */
- videobuf_queue_dma_contig_init(&common->buffer_queue,
- &video_qops, NULL,
- &common->irqlock,
- reqbuf->type, field,
- sizeof(struct videobuf_buffer), fh,
- &common->lock);
+ vb2_queue_init(q);
/* Set io allowed member of file handle to TRUE */
fh->io_allowed[index] = 1;
@@ -855,9 +979,8 @@ static int vpif_reqbufs(struct file *file, void *priv,
/* Store type of memory requested in channel object */
common->memory = reqbuf->memory;
INIT_LIST_HEAD(&common->dma_queue);
-
/* Allocate buffers */
- return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+ return vb2_reqbufs(&common->buffer_queue, reqbuf);
}
static int vpif_querybuf(struct file *file, void *priv,
@@ -870,22 +993,25 @@ static int vpif_querybuf(struct file *file, void *priv,
if (common->fmt.type != tbuf->type)
return -EINVAL;
- return videobuf_querybuf(&common->buffer_queue, tbuf);
+ return vb2_querybuf(&common->buffer_queue, tbuf);
}
static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
+ struct vpif_fh *fh = NULL;
+ struct channel_obj *ch = NULL;
+ struct common_obj *common = NULL;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct v4l2_buffer tbuf = *buf;
- struct videobuf_buffer *buf1;
- unsigned long addr = 0;
- unsigned long flags;
- int ret = 0;
+ if (!buf || !priv)
+ return -EINVAL;
+
+ fh = priv;
+ ch = fh->channel;
+ if (!ch)
+ return -EINVAL;
- if (common->fmt.type != tbuf.type)
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ if (common->fmt.type != buf->type)
return -EINVAL;
if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
@@ -893,73 +1019,7 @@ static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
return -EACCES;
}
- if (!(list_empty(&common->dma_queue)) ||
- (common->cur_frm != common->next_frm) ||
- !(common->started) ||
- (common->started && (0 == ch->field_id)))
- return videobuf_qbuf(&common->buffer_queue, buf);
-
- /* bufferqueue is empty store buffer address in VPIF registers */
- mutex_lock(&common->buffer_queue.vb_lock);
- buf1 = common->buffer_queue.bufs[tbuf.index];
- if (buf1->memory != tbuf.memory) {
- vpif_err("invalid buffer type\n");
- goto qbuf_exit;
- }
-
- if ((buf1->state == VIDEOBUF_QUEUED) ||
- (buf1->state == VIDEOBUF_ACTIVE)) {
- vpif_err("invalid state\n");
- goto qbuf_exit;
- }
-
- switch (buf1->memory) {
- case V4L2_MEMORY_MMAP:
- if (buf1->baddr == 0)
- goto qbuf_exit;
- break;
-
- case V4L2_MEMORY_USERPTR:
- if (tbuf.length < buf1->bsize)
- goto qbuf_exit;
-
- if ((VIDEOBUF_NEEDS_INIT != buf1->state)
- && (buf1->baddr != tbuf.m.userptr)) {
- vpif_buffer_release(&common->buffer_queue, buf1);
- buf1->baddr = tbuf.m.userptr;
- }
- break;
-
- default:
- goto qbuf_exit;
- }
-
- local_irq_save(flags);
- ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
- common->buffer_queue.field);
- if (ret < 0) {
- local_irq_restore(flags);
- goto qbuf_exit;
- }
-
- buf1->state = VIDEOBUF_ACTIVE;
- addr = buf1->boff;
- common->next_frm = buf1;
- if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
- common->set_addr((addr + common->ytop_off),
- (addr + common->ybtm_off),
- (addr + common->ctop_off),
- (addr + common->cbtm_off));
- }
-
- local_irq_restore(flags);
- list_add_tail(&buf1->stream, &common->buffer_queue.stream);
- mutex_unlock(&common->buffer_queue.vb_lock);
- return 0;
-
-qbuf_exit:
- mutex_unlock(&common->buffer_queue.vb_lock);
- return -EINVAL;
+ return vb2_qbuf(&common->buffer_queue, buf);
}
static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
@@ -969,7 +1029,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
int ret = 0;
- if (!(*std_id & DM646X_V4L2_STD))
+ if (!(*std_id & VPIF_V4L2_STD))
return -EINVAL;
if (common->started) {
@@ -1026,7 +1086,7 @@ static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- return videobuf_dqbuf(&common->buffer_queue, p,
+ return vb2_dqbuf(&common->buffer_queue, p,
(file->f_flags & O_NONBLOCK));
}
@@ -1037,10 +1097,6 @@ static int vpif_streamon(struct file *file, void *priv,
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
- struct vpif_params *vpif = &ch->vpifparams;
- struct vpif_display_config *vpif_config_data =
- vpif_dev->platform_data;
- unsigned long addr = 0;
int ret = 0;
if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
@@ -1072,82 +1128,13 @@ static int vpif_streamon(struct file *file, void *priv,
if (ret < 0)
return ret;
- /* Call videobuf_streamon to start streaming in videobuf */
- ret = videobuf_streamon(&common->buffer_queue);
+ /* Call vb2_streamon to start streaming in videobuf2 */
+ ret = vb2_streamon(&common->buffer_queue, buftype);
if (ret < 0) {
- vpif_err("videobuf_streamon\n");
+ vpif_err("vb2_streamon\n");
return ret;
}
- /* If buffer queue is empty, return error */
- if (list_empty(&common->dma_queue)) {
- vpif_err("buffer queue is empty\n");
- return -EIO;
- }
-
- /* Get the next frame from the buffer queue */
- common->next_frm = common->cur_frm =
- list_entry(common->dma_queue.next,
- struct videobuf_buffer, queue);
-
- list_del(&common->cur_frm->queue);
- /* Mark state of the current frame to active */
- common->cur_frm->state = VIDEOBUF_ACTIVE;
-
- /* Initialize field_id and started member */
- ch->field_id = 0;
- common->started = 1;
- if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- addr = common->cur_frm->boff;
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((ch->vpifparams.std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
- && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
- || (!ch->vpifparams.std_info.frm_fmt
- && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_err("conflict in field format and std format\n");
- return -EINVAL;
- }
-
- /* clock settings */
- ret =
- vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
- ch->vpifparams.std_info.hd_sd);
- if (ret < 0) {
- vpif_err("can't set clock\n");
- return ret;
- }
-
- /* set the parameters and addresses */
- ret = vpif_set_video_params(vpif, ch->channel_id + 2);
- if (ret < 0)
- return ret;
-
- common->started = ret;
- vpif_config_addr(ch, ret);
- common->set_addr((addr + common->ytop_off),
- (addr + common->ybtm_off),
- (addr + common->ctop_off),
- (addr + common->cbtm_off));
-
- /* Set interrupt for both the fields in VPIF
- Register enable channel in VPIF register */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- channel2_intr_assert();
- channel2_intr_enable(1);
- enable_channel2(1);
- }
-
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
- || (common->started == 2)) {
- channel3_intr_assert();
- channel3_intr_enable(1);
- enable_channel3(1);
- }
- channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
- }
return ret;
}
@@ -1157,6 +1144,8 @@ static int vpif_streamoff(struct file *file, void *priv,
struct vpif_fh *fh = priv;
struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_display_config *vpif_config_data =
+ vpif_dev->platform_data;
if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
vpif_err("buffer type not supported\n");
@@ -1176,18 +1165,22 @@ static int vpif_streamoff(struct file *file, void *priv,
if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
/* disable channel */
if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ if (vpif_config_data->ch2_clip_en)
+ channel2_clipping_enable(0);
enable_channel2(0);
channel2_intr_enable(0);
}
if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
(2 == common->started)) {
+ if (vpif_config_data->ch3_clip_en)
+ channel3_clipping_enable(0);
enable_channel3(0);
channel3_intr_enable(0);
}
}
common->started = 0;
- return videobuf_streamoff(&common->buffer_queue);
+ return vb2_streamoff(&common->buffer_queue, buftype);
}
static int vpif_cropcap(struct file *file, void *priv,
@@ -1220,7 +1213,7 @@ static int vpif_enum_output(struct file *file, void *fh,
strcpy(output->name, config->output[output->index]);
output->type = V4L2_OUTPUT_TYPE_ANALOG;
- output->std = DM646X_V4L2_STD;
+ output->std = VPIF_V4L2_STD;
return 0;
}
@@ -1605,7 +1598,7 @@ static struct video_device vpif_video_template = {
.name = "vpif",
.fops = &vpif_fops,
.ioctl_ops = &vpif_ioctl_ops,
- .tvnorms = DM646X_V4L2_STD,
+ .tvnorms = VPIF_V4L2_STD,
.current_norm = V4L2_STD_625_50,
};
@@ -1687,9 +1680,9 @@ static __init int vpif_probe(struct platform_device *pdev)
struct video_device *vfd;
struct resource *res;
int subdev_count;
+ size_t size;
vpif_dev = &pdev->dev;
-
err = initialize_vpif();
if (err) {
@@ -1706,8 +1699,8 @@ static __init int vpif_probe(struct platform_device *pdev)
k = 0;
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
- "DM646x_Display",
+ if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+ "VPIF_Display",
(void *)(&vpif_obj.dev[k]->channel_id))) {
err = -EBUSY;
goto vpif_int_err;
@@ -1737,13 +1730,31 @@ static __init int vpif_probe(struct platform_device *pdev)
vfd->v4l2_dev = &vpif_obj.v4l2_dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name),
- "DM646x_VPIFDisplay_DRIVER_V%s",
+ "VPIF_Display_DRIVER_V%s",
VPIF_DISPLAY_VERSION);
/* Set video_dev to the video device */
ch->video_dev = vfd;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ size = resource_size(res);
+ /* The resources are divided into two equal memory and when
+ * we have HD output we can add them together
+ */
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+
+ /* only enabled if second resource exists */
+ config_params.video_limit[ch->channel_id] = 0;
+ if (size)
+ config_params.video_limit[ch->channel_id] =
+ size/2;
+ }
+ }
+
for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
ch = vpif_obj.dev[j];
/* Initialize field of the channel objects */
@@ -1823,7 +1834,7 @@ static __init int vpif_probe(struct platform_device *pdev)
}
v4l2_info(&vpif_obj.v4l2_dev,
- "DM646x VPIF display driver initialized\n");
+ " VPIF display driver initialized\n");
return 0;
probe_subdev_out:
@@ -1871,10 +1882,81 @@ static int vpif_remove(struct platform_device *device)
return 0;
}
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (atomic_read(&ch->usrs) && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ common->started == 2) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (atomic_read(&ch->usrs) && common->io_usrs) {
+ /* Enable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(1);
+ channel2_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ common->started == 2) {
+ enable_channel3(1);
+ channel3_intr_enable(1);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
static __refdata struct platform_driver vpif_driver = {
.driver = {
.name = "vpif_display",
.owner = THIS_MODULE,
+ .pm = vpif_pm_ops,
},
.probe = vpif_probe,
.remove = vpif_remove,
diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h
index 56879d1a0684..8967ffb44058 100644
--- a/drivers/media/video/davinci/vpif_display.h
+++ b/drivers/media/video/davinci/vpif_display.h
@@ -1,5 +1,5 @@
/*
- * DM646x display header file
+ * VPIF display header file
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
@@ -21,7 +21,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/davinci/vpif_types.h>
#include "vpif.h"
@@ -73,21 +73,29 @@ struct vbi_obj {
* vbi data */
};
+struct vpif_disp_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
struct common_obj {
/* Buffer specific parameters */
u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
* storing frames */
u32 numbuffers; /* number of buffers */
- struct videobuf_buffer *cur_frm; /* Pointer pointing to current
- * videobuf_buffer */
- struct videobuf_buffer *next_frm; /* Pointer pointing to next
- * videobuf_buffer */
+ struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
+ * vb2_buffer */
+ struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
+ * vb2_buffer */
enum v4l2_memory memory; /* This field keeps track of
* type of buffer exchange
* method user has selected */
struct v4l2_format fmt; /* Used to store the format */
- struct videobuf_queue buffer_queue; /* Buffer queue used in
+ struct vb2_queue buffer_queue; /* Buffer queue used in
* video-buf */
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+
struct list_head dma_queue; /* Queue of filled frames */
spinlock_t irqlock; /* Used in video-buf */
@@ -158,6 +166,7 @@ struct vpif_config_params {
u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
+ u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
u8 min_numbuffers;
};
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index d7e2a3dc5525..07dc594e79f0 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -42,6 +42,7 @@
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -679,19 +680,19 @@ static int em28xx_audio_init(struct em28xx *dev)
INIT_WORK(&dev->wq_trigger, audio_trigger);
if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
- em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL);
- em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL);
- em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL);
- em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL);
- em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL);
- em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL);
- em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL);
-
- em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL);
- em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL);
- em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL);
- em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL);
- em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL);
+ em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
+ em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
+ em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
+ em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
+ em28xx_cvol_new(card, dev, "CD", AC97_CD);
+ em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
+ em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
+
+ em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
+ em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
+ em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
+ em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
+ em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
}
err = snd_card_register(card);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 862c6575c557..ca62b9981380 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -975,12 +975,7 @@ struct em28xx_board em28xx_boards[] = {
.name = "Terratec Cinergy HTC Stick",
.has_dvb = 1,
.ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
-#if 0
- .tuner_type = TUNER_PHILIPS_TDA8290,
- .tuner_addr = 0x41,
- .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
- .tuner_gpio = terratec_h5_gpio,
-#endif
+ .tuner_type = TUNER_ABSENT,
.i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 5717bdee8f1b..de2cb20ad2cc 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
+#include <sound/ac97_codec.h>
#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -326,13 +327,13 @@ struct em28xx_vol_itable {
};
static struct em28xx_vol_itable inputs[] = {
- { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL },
- { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL },
- { EM28XX_AMUX_PHONE, AC97_PHONE_VOL },
- { EM28XX_AMUX_MIC, AC97_MIC_VOL },
- { EM28XX_AMUX_CD, AC97_CD_VOL },
- { EM28XX_AMUX_AUX, AC97_AUX_VOL },
- { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL },
+ { EM28XX_AMUX_VIDEO, AC97_VIDEO },
+ { EM28XX_AMUX_LINE_IN, AC97_LINE },
+ { EM28XX_AMUX_PHONE, AC97_PHONE },
+ { EM28XX_AMUX_MIC, AC97_MIC },
+ { EM28XX_AMUX_CD, AC97_CD },
+ { EM28XX_AMUX_AUX, AC97_AUX },
+ { EM28XX_AMUX_PCM_OUT, AC97_PCM },
};
static int set_ac97_input(struct em28xx *dev)
@@ -415,11 +416,11 @@ struct em28xx_vol_otable {
};
static const struct em28xx_vol_otable outputs[] = {
- { EM28XX_AOUT_MASTER, AC97_MASTER_VOL },
- { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL },
- { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL },
- { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL },
- { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL },
+ { EM28XX_AOUT_MASTER, AC97_MASTER },
+ { EM28XX_AOUT_LINE, AC97_HEADPHONE },
+ { EM28XX_AOUT_MONO, AC97_MASTER_MONO },
+ { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER },
+ { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER },
};
int em28xx_audio_analog_set(struct em28xx *dev)
@@ -459,9 +460,9 @@ int em28xx_audio_analog_set(struct em28xx *dev)
if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
int vol;
- em28xx_write_ac97(dev, AC97_POWER_DOWN_CTRL, 0x4200);
- em28xx_write_ac97(dev, AC97_EXT_AUD_CTRL, 0x0031);
- em28xx_write_ac97(dev, AC97_PCM_IN_SRATE, 0xbb80);
+ em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200);
+ em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031);
+ em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80);
/* LSB: left channel - both channels with the same level */
vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
@@ -487,7 +488,7 @@ int em28xx_audio_analog_set(struct em28xx *dev)
channels */
sel |= (sel << 8);
- em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel);
+ em28xx_write_ac97(dev, AC97_REC_SEL, sel);
}
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 16410ac20092..a16531fa937a 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -310,31 +310,47 @@ static struct drxd_config em28xx_drxd = {
.disable_i2c_gate_ctrl = 1,
};
-struct drxk_config terratec_h5_drxk = {
+static struct drxk_config terratec_h5_drxk = {
.adr = 0x29,
.single_master = 1,
.no_i2c_bridge = 1,
.microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+ .qam_demod_parameter_count = 2,
};
-struct drxk_config hauppauge_930c_drxk = {
+static struct drxk_config hauppauge_930c_drxk = {
.adr = 0x29,
.single_master = 1,
.no_i2c_bridge = 1,
.microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw",
.chunk_size = 56,
+ .qam_demod_parameter_count = 2,
};
-struct drxk_config maxmedia_ub425_tc_drxk = {
+struct drxk_config terratec_htc_stick_drxk = {
.adr = 0x29,
.single_master = 1,
.no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw",
+ .chunk_size = 54,
+ .qam_demod_parameter_count = 2,
+ /* Required for the antenna_gpio to disable LNA. */
+ .antenna_dvbt = true,
+ /* The windows driver uses the same. This will disable LNA. */
+ .antenna_gpio = 0x6,
};
-struct drxk_config pctv_520e_drxk = {
+static struct drxk_config maxmedia_ub425_tc_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+};
+
+static struct drxk_config pctv_520e_drxk = {
.adr = 0x29,
.single_master = 1,
.microcode_name = "dvb-demod-drxk-pctv.fw",
+ .qam_demod_parameter_count = 2,
.chunk_size = 58,
.antenna_dvbt = true, /* disable LNA */
.antenna_gpio = (1 << 2), /* disable LNA */
@@ -473,6 +489,57 @@ static void terratec_h5_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_h5_end);
};
+static void terratec_htc_stick_init(struct em28xx *dev)
+{
+ int i;
+
+ /*
+ * GPIO configuration:
+ * 0xff: unknown (does not affect DVB-T).
+ * 0xf6: DRX-K (demodulator).
+ * 0xe6: unknown (does not affect DVB-T).
+ * 0xb6: unknown (does not affect DVB-T).
+ */
+ struct em28xx_reg_seq terratec_htc_stick_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_htc_stick_end[] = {
+ {EM2874_R80_GPIO, 0xb6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ { -1, -1, -1, -1},
+ };
+
+ /* Init the analog decoder? */
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ };
+
+ em28xx_gpio_set(dev, terratec_htc_stick_init);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+
+ em28xx_gpio_set(dev, terratec_htc_stick_end);
+};
+
static void pctv_520e_init(struct em28xx *dev)
{
/*
@@ -944,7 +1011,6 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
}
case EM2884_BOARD_TERRATEC_H5:
- case EM2884_BOARD_CINERGY_HTC_STICK:
terratec_h5_init(dev);
dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap);
@@ -1021,6 +1087,25 @@ static int em28xx_dvb_init(struct em28xx *dev)
}
}
break;
+ case EM2884_BOARD_CINERGY_HTC_STICK:
+ terratec_htc_stick_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
+ &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* Attach the demodulator. */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 185db65b766e..1683bd9d51ee 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -475,6 +475,7 @@ static struct i2c_client em28xx_client_template = {
*/
static char *i2c_devs[128] = {
[0x4a >> 1] = "saa7113h",
+ [0x52 >> 1] = "drxk",
[0x60 >> 1] = "remote IR sensor",
[0x8e >> 1] = "remote IR sensor",
[0x86 >> 1] = "tda9887",
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index 5e30c4f3f248..97d36b4f19db 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -345,7 +345,7 @@ static void em28xx_ir_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
}
-int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
+static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
{
int rc = 0;
struct em28xx_IR *ir = rc_dev->priv;
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index 2f6268505726..6ff368297f6e 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -211,58 +211,9 @@ enum em28xx_chip_id {
};
/*
- * Registers used by em202 and other AC97 chips
+ * Registers used by em202
*/
-/* Standard AC97 registers */
-#define AC97_RESET 0x00
-
- /* Output volumes */
-#define AC97_MASTER_VOL 0x02
-#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */
-#define AC97_MASTER_MONO_VOL 0x06
-
- /* Input volumes */
-#define AC97_PC_BEEP_VOL 0x0a
-#define AC97_PHONE_VOL 0x0c
-#define AC97_MIC_VOL 0x0e
-#define AC97_LINEIN_VOL 0x10
-#define AC97_CD_VOL 0x12
-#define AC97_VIDEO_VOL 0x14
-#define AC97_AUX_VOL 0x16
-#define AC97_PCM_OUT_VOL 0x18
-
- /* capture registers */
-#define AC97_RECORD_SELECT 0x1a
-#define AC97_RECORD_GAIN 0x1c
-
- /* control registers */
-#define AC97_GENERAL_PURPOSE 0x20
-#define AC97_3D_CTRL 0x22
-#define AC97_AUD_INT_AND_PAG 0x24
-#define AC97_POWER_DOWN_CTRL 0x26
-#define AC97_EXT_AUD_ID 0x28
-#define AC97_EXT_AUD_CTRL 0x2a
-
-/* Supported rate varies for each AC97 device
- if write an unsupported value, it will return the closest one
- */
-#define AC97_PCM_OUT_FRONT_SRATE 0x2c
-#define AC97_PCM_OUT_SURR_SRATE 0x2e
-#define AC97_PCM_OUT_LFE_SRATE 0x30
-#define AC97_PCM_IN_SRATE 0x32
-
- /* For devices with more than 2 channels, extra output volumes */
-#define AC97_LFE_MASTER_VOL 0x36
-#define AC97_SURR_MASTER_VOL 0x38
-
- /* Digital SPDIF output control */
-#define AC97_SPDIF_OUT_CTRL 0x3a
-
- /* Vendor ID identifier */
-#define AC97_VENDOR_ID1 0x7c
-#define AC97_VENDOR_ID2 0x7e
-
/* EMP202 vendor registers */
#define EM202_EXT_MODEM_CTRL 0x3e
#define EM202_GPIO_CONF 0x4c
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
index 9769f17915c0..352f32190e68 100644
--- a/drivers/media/video/gspca/benq.c
+++ b/drivers/media/video/gspca/benq.c
@@ -33,10 +33,6 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
};
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -256,8 +252,6 @@ static void sd_isoc_irq(struct urb *urb)
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -288,6 +282,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index f39fee0fd10f..c9052f20435e 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -31,74 +31,18 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
MODULE_LICENSE("GPL");
+#define QUALITY 50
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
-
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
- u8 quality;
-#define QUALITY_MIN 30
-#define QUALITY_MAX 60
-#define QUALITY_DEF 40
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *sat;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 0xd4
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0x0a,
- .maximum = 0x1f,
- .step = 1,
-#define CONTRAST_DEF 0x0c
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 7,
- .step = 1,
-#define COLOR_DEF 3
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
@@ -817,17 +761,11 @@ static void cx11646_init1(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
-
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->quality = QUALITY_DEF;
return 0;
}
@@ -849,7 +787,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, QUALITY);
cx11646_initsize(gspca_dev);
cx11646_fw(gspca_dev);
@@ -903,142 +841,99 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val, s32 sat)
{
- struct sd *sd = (struct sd *) gspca_dev;
__u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
__u8 reg51c[2];
- __u8 bright;
- __u8 colors;
- bright = sd->brightness;
- regE5cbx[2] = bright;
+ regE5cbx[2] = val;
reg_w(gspca_dev, 0x00e5, regE5cbx, 8);
reg_r(gspca_dev, 0x00e8, 8);
reg_w(gspca_dev, 0x00e5, regE5c, 4);
reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
- colors = sd->colors;
reg51c[0] = 0x77;
- reg51c[1] = colors;
+ reg51c[1] = sat;
reg_w(gspca_dev, 0x0051, reg51c, 2);
reg_w(gspca_dev, 0x0010, reg10, 2);
reg_w_val(gspca_dev, 0x0070, reg70);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val, s32 sat)
{
- struct sd *sd = (struct sd *) gspca_dev;
__u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */
/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */
__u8 reg51c[2];
- regE5acx[2] = sd->contrast;
+ regE5acx[2] = val;
reg_w(gspca_dev, 0x00e5, regE5acx, 4);
reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
reg51c[0] = 0x77;
- reg51c[1] = sd->colors;
+ reg51c[1] = sat;
reg_w(gspca_dev, 0x0051, reg51c, 2);
reg_w(gspca_dev, 0x0010, reg10, 2);
reg_w_val(gspca_dev, 0x0070, reg70);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- *val = sd->contrast;
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- sd->colors = val;
- if (gspca_dev->streaming) {
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val, sd->sat->cur.val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val, sd->sat->cur.val);
+ break;
+ case V4L2_CID_SATURATION:
+ setbrightness(gspca_dev, sd->brightness->cur.val, ctrl->val);
+ setcontrast(gspca_dev, sd->contrast->cur.val, ctrl->val);
+ break;
}
- return 0;
+ return gspca_dev->usb_err;
}
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 0xd4);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0x0a, 0x1f, 1, 0x0c);
+ sd->sat = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 7, 1, 3);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
@@ -1064,6 +959,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
index 8f33bbd091ad..2499a881d9a3 100644
--- a/drivers/media/video/gspca/cpia1.c
+++ b/drivers/media/video/gspca/cpia1.c
@@ -225,6 +225,15 @@ MODULE_LICENSE("GPL");
#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
sd->params.version.firmwareRevision == (y))
+#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
+#define BRIGHTNESS_DEF 50
+#define CONTRAST_DEF 48
+#define SATURATION_DEF 50
+#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+#define ILLUMINATORS_1_DEF 0
+#define ILLUMINATORS_2_DEF 0
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+
/* Developer's Guide Table 5 p 3-34
* indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
static u8 flicker_jumps[2][2][4] =
@@ -360,135 +369,9 @@ struct sd {
atomic_t fps;
int exposure_count;
u8 exposure_status;
+ struct v4l2_ctrl *freq;
u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
u8 first_frame;
- u8 freq;
-};
-
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
-#define BRIGHTNESS_IDX 0
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 100,
- .step = 1,
-#define BRIGHTNESS_DEF 50
- .default_value = BRIGHTNESS_DEF,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define CONTRAST_IDX 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 96,
- .step = 8,
-#define CONTRAST_DEF 48
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define SATURATION_IDX 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 100,
- .step = 1,
-#define SATURATION_DEF 50
- .default_value = SATURATION_DEF,
- },
- .set = sd_setsaturation,
- .get = sd_getsaturation,
- },
-#define POWER_LINE_FREQUENCY_IDX 3
- {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
-#define FREQ_DEF 1
- .default_value = FREQ_DEF,
- },
- .set = sd_setfreq,
- .get = sd_getfreq,
- },
-#define ILLUMINATORS_1_IDX 4
- {
- {
- .id = V4L2_CID_ILLUMINATORS_1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Illuminator 1",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define ILLUMINATORS_1_DEF 0
- .default_value = ILLUMINATORS_1_DEF,
- },
- .set = sd_setilluminator1,
- .get = sd_getilluminator1,
- },
-#define ILLUMINATORS_2_IDX 5
- {
- {
- .id = V4L2_CID_ILLUMINATORS_2,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Illuminator 2",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define ILLUMINATORS_2_DEF 0
- .default_value = ILLUMINATORS_2_DEF,
- },
- .set = sd_setilluminator2,
- .get = sd_getilluminator2,
- },
-#define COMP_TARGET_IDX 6
- {
- {
-#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
- .id = V4L2_CID_COMP_TARGET,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Compression Target",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
- .default_value = COMP_TARGET_DEF,
- },
- .set = sd_setcomptarget,
- .get = sd_getcomptarget,
- },
};
static const struct v4l2_pix_format mode[] = {
@@ -770,15 +653,6 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
params->apcor.gain2 = 0x16;
params->apcor.gain4 = 0x24;
params->apcor.gain8 = 0x34;
- params->flickerControl.flickerMode = 0;
- params->flickerControl.disabled = 1;
-
- params->flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [params->sensorFps.baserate]
- [params->sensorFps.divisor];
- params->flickerControl.allowableOverExposure =
- find_over_exposure(params->colourParams.brightness);
params->vlOffset.gain1 = 20;
params->vlOffset.gain2 = 24;
params->vlOffset.gain4 = 26;
@@ -798,6 +672,15 @@ static void reset_camera_params(struct gspca_dev *gspca_dev)
params->sensorFps.divisor = 1;
params->sensorFps.baserate = 1;
+ params->flickerControl.flickerMode = 0;
+ params->flickerControl.disabled = 1;
+ params->flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [params->sensorFps.baserate]
+ [params->sensorFps.divisor];
+ params->flickerControl.allowableOverExposure =
+ find_over_exposure(params->colourParams.brightness);
+
params->yuvThreshold.yThreshold = 6; /* From windows driver */
params->yuvThreshold.uvThreshold = 6; /* From windows driver */
@@ -1110,9 +993,6 @@ static int command_setlights(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int ret, p1, p2;
- if (!sd->params.qx3.qx3_detected)
- return 0;
-
p1 = (sd->params.qx3.bottomlight == 0) << 1;
p2 = (sd->params.qx3.toplight == 0) << 3;
@@ -1551,8 +1431,10 @@ static void restart_flicker(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
+ struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
+ sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
reset_camera_params(gspca_dev);
PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
@@ -1562,8 +1444,25 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = mode;
cam->nmodes = ARRAY_SIZE(mode);
- sd_setfreq(gspca_dev, FREQ_DEF);
+ goto_low_power(gspca_dev);
+ /* Check the firmware version. */
+ sd->params.version.firmwareVersion = 0;
+ get_version_information(gspca_dev);
+ if (sd->params.version.firmwareVersion != 1) {
+ PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
+ sd->params.version.firmwareVersion);
+ return -ENODEV;
+ }
+ /* A bug in firmware 1-02 limits gainMode to 2 */
+ if (sd->params.version.firmwareRevision <= 2 &&
+ sd->params.exposure.gainMode > 2) {
+ sd->params.exposure.gainMode = 2;
+ }
+
+ /* set QX3 detected flag */
+ sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+ sd->params.pnpID.product == 0x0001);
return 0;
}
@@ -1602,21 +1501,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* Check the firmware version. */
sd->params.version.firmwareVersion = 0;
get_version_information(gspca_dev);
- if (sd->params.version.firmwareVersion != 1) {
- PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
- sd->params.version.firmwareVersion);
- return -ENODEV;
- }
-
- /* A bug in firmware 1-02 limits gainMode to 2 */
- if (sd->params.version.firmwareRevision <= 2 &&
- sd->params.exposure.gainMode > 2) {
- sd->params.exposure.gainMode = 2;
- }
-
- /* set QX3 detected flag */
- sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
- sd->params.pnpID.product == 0x0001);
/* The fatal error checking should be done after
* the camera powers up (developer's guide p 3-38) */
@@ -1785,9 +1669,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
or disable the illuminator controls, if this isn't a QX3 */
if (sd->params.qx3.qx3_detected)
command_setlights(gspca_dev);
- else
- gspca_dev->ctrl_dis |=
- ((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));
sd_stopN(gspca_dev);
@@ -1871,235 +1752,123 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- sd->params.colourParams.brightness = val;
- sd->params.flickerControl.allowableOverExposure =
- find_over_exposure(sd->params.colourParams.brightness);
- if (gspca_dev->streaming) {
- ret = command_setcolourparams(gspca_dev);
- if (ret)
- return ret;
- return command_setflickerctrl(gspca_dev);
- }
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->params.colourParams.brightness;
- return 0;
-}
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->usb_err = 0;
- sd->params.colourParams.contrast = val;
- if (gspca_dev->streaming)
- return command_setcolourparams(gspca_dev);
-
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->params.colourParams.contrast;
- return 0;
-}
-
-static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->params.colourParams.saturation = val;
- if (gspca_dev->streaming)
- return command_setcolourparams(gspca_dev);
-
- return 0;
-}
-
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->params.colourParams.saturation;
- return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int on;
+ if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+ return 0;
- switch (val) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- on = 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ sd->params.colourParams.brightness = ctrl->val;
+ sd->params.flickerControl.allowableOverExposure =
+ find_over_exposure(sd->params.colourParams.brightness);
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+ if (!gspca_dev->usb_err)
+ gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
break;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- on = 1;
- sd->mainsFreq = 0;
+ case V4L2_CID_CONTRAST:
+ sd->params.colourParams.contrast = ctrl->val;
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
break;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- on = 1;
- sd->mainsFreq = 1;
+ case V4L2_CID_SATURATION:
+ sd->params.colourParams.saturation = ctrl->val;
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
break;
- default:
- return -EINVAL;
- }
-
- sd->freq = val;
- sd->params.flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [sd->params.sensorFps.baserate]
- [sd->params.sensorFps.divisor];
-
- return set_flicker(gspca_dev, on, gspca_dev->streaming);
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->freq;
- return 0;
-}
-
-static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->params.compressionTarget.frTargeting = val;
- if (gspca_dev->streaming)
- return command_setcompressiontarget(gspca_dev);
-
- return 0;
-}
-
-static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->params.compressionTarget.frTargeting;
- return 0;
-}
-
-static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- if (!sd->params.qx3.qx3_detected)
- return -EINVAL;
-
- switch (n) {
- case 1:
- sd->params.qx3.bottomlight = val ? 1 : 0;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+
+ gspca_dev->usb_err = set_flicker(gspca_dev,
+ ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+ gspca_dev->streaming);
break;
- case 2:
- sd->params.qx3.toplight = val ? 1 : 0;
+ case V4L2_CID_ILLUMINATORS_1:
+ sd->params.qx3.bottomlight = ctrl->val;
+ gspca_dev->usb_err = command_setlights(gspca_dev);
break;
- default:
- return -EINVAL;
- }
-
- ret = command_setlights(gspca_dev);
- if (ret && ret != -EINVAL)
- ret = -EBUSY;
-
- return ret;
-}
-
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
-{
- return sd_setilluminator(gspca_dev, val, 1);
-}
-
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
-{
- return sd_setilluminator(gspca_dev, val, 2);
-}
-
-static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!sd->params.qx3.qx3_detected)
- return -EINVAL;
-
- switch (n) {
- case 1:
- *val = sd->params.qx3.bottomlight;
+ case V4L2_CID_ILLUMINATORS_2:
+ sd->params.qx3.toplight = ctrl->val;
+ gspca_dev->usb_err = command_setlights(gspca_dev);
break;
- case 2:
- *val = sd->params.qx3.toplight;
+ case CPIA1_CID_COMP_TARGET:
+ sd->params.compressionTarget.frTargeting = ctrl->val;
+ gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
break;
- default:
- return -EINVAL;
}
- return 0;
+ return gspca_dev->usb_err;
}
-static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
-{
- return sd_getilluminator(gspca_dev, val, 1);
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- return sd_getilluminator(gspca_dev, val, 2);
-}
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const char * const comp_target_menu[] = {
+ "Quality",
+ "Framerate",
+ NULL
+ };
+ static const struct v4l2_ctrl_config comp_target = {
+ .ops = &sd_ctrl_ops,
+ .id = CPIA1_CID_COMP_TARGET,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Compression Target",
+ .qmenu = comp_target_menu,
+ .max = 1,
+ .def = COMP_TARGET_DEF,
+ };
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 7);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ FREQ_DEF);
+ if (sd->params.qx3.qx3_detected) {
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
+ ILLUMINATORS_1_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
+ ILLUMINATORS_2_DEF);
+ }
+ v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
- break;
- case V4L2_CID_COMP_TARGET:
- switch (menu->index) {
- case CPIA_COMPRESSION_TARGET_QUALITY:
- strcpy((char *) menu->name, "Quality");
- return 0;
- case CPIA_COMPRESSION_TARGET_FRAMERATE:
- strcpy((char *) menu->name, "Framerate");
- return 0;
- }
- break;
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
}
- return -EINVAL;
+ return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.dq_callback = sd_dq_callback,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.other_input = 1,
#endif
@@ -2129,6 +1898,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 81a4adbd9f7c..38f68e11c3a2 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -32,9 +32,6 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
unsigned char autogain;
char sensor;
@@ -44,76 +41,6 @@ struct sd {
#define AG_CNT_START 13
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 1,
- .maximum = 127,
- .step = 1,
-#define BRIGHTNESS_DEF 63
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define CONTRAST_DEF 127
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define COLOR_IDX 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
-#define COLOR_DEF 7
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -464,36 +391,31 @@ static void Et_init2(struct gspca_dev *gspca_dev)
reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
int i;
- __u8 brightness = sd->brightness;
for (i = 0; i < 4; i++)
- reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+ reg_w_val(gspca_dev, ET_O_RED + i, val);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
__u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
- __u8 contrast = sd->contrast;
- memset(RGBG, contrast, sizeof(RGBG) - 2);
+ memset(RGBG, val, sizeof(RGBG) - 2);
reg_w(gspca_dev, ET_G_RED, RGBG, 6);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
__u8 i2cflags = 0x01;
/* __u8 green = 0; */
- __u8 colors = sd->colors;
- I2cc[3] = colors; /* red */
- I2cc[0] = 15 - colors; /* blue */
+ I2cc[3] = val; /* red */
+ I2cc[0] = 15 - val; /* blue */
/* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
/* I2cc[1] = I2cc[2] = green; */
if (sd->sensor == SENSOR_PAS106) {
@@ -504,15 +426,16 @@ static void setcolors(struct gspca_dev *gspca_dev)
I2cc[3], I2cc[0], green); */
}
-static void getcolors(struct gspca_dev *gspca_dev)
+static s32 getcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_PAS106) {
/* i2c_r(gspca_dev, PAS106_REG9); * blue */
i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */
- sd->colors = gspca_dev->usb_buf[0] & 0x0f;
+ return gspca_dev->usb_buf[0] & 0x0f;
}
+ return 0;
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -622,8 +545,7 @@ static void Et_init1(struct gspca_dev *gspca_dev)
i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
/* now set by fifo the whole colors setting */
reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
- getcolors(gspca_dev);
- setcolors(gspca_dev);
+ setcolors(gspca_dev, getcolors(gspca_dev));
}
/* this function is called at probe time */
@@ -641,12 +563,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
} else {
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
- gspca_dev->ctrl_dis = (1 << COLOR_IDX);
}
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
return 0;
}
@@ -780,85 +697,68 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- if (gspca_dev->streaming)
+ 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) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ sd->autogain = ctrl->val;
setautogain(gspca_dev);
- return 0;
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
- *val = sd->autogain;
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 1, 127, 1, 63);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ if (sd->sensor == SENSOR_PAS106)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 15, 1, 7);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -892,6 +792,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 6e26c93b4656..c8f2201cc35a 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -299,6 +299,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index c549574c1c7e..ced3b71f14e5 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -521,6 +521,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 31721eadc597..d4e8343f5b10 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -930,6 +930,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
}
gspca_dev->streaming = 1;
+ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
/* some bulk transfers are started by the subdriver */
if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
@@ -1049,12 +1050,6 @@ static int vidioc_g_register(struct file *file, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- if (!gspca_dev->sd_desc->get_chip_ident)
- return -ENOTTY;
-
- if (!gspca_dev->sd_desc->get_register)
- return -ENOTTY;
-
gspca_dev->usb_err = 0;
return gspca_dev->sd_desc->get_register(gspca_dev, reg);
}
@@ -1064,12 +1059,6 @@ static int vidioc_s_register(struct file *file, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- if (!gspca_dev->sd_desc->get_chip_ident)
- return -ENOTTY;
-
- if (!gspca_dev->sd_desc->set_register)
- return -ENOTTY;
-
gspca_dev->usb_err = 0;
return gspca_dev->sd_desc->set_register(gspca_dev, reg);
}
@@ -1080,9 +1069,6 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- if (!gspca_dev->sd_desc->get_chip_ident)
- return -ENOTTY;
-
gspca_dev->usb_err = 0;
return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
}
@@ -1136,8 +1122,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
int mode;
mode = gspca_dev->curr_mode;
- memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
- sizeof fmt->fmt.pix);
+ fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+ /* some drivers use priv internally, zero it before giving it to
+ userspace */
+ fmt->fmt.pix.priv = 0;
return 0;
}
@@ -1168,8 +1156,10 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
/* else
; * no chance, return this mode */
}
- memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
- sizeof fmt->fmt.pix);
+ fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+ /* some drivers use priv internally, zero it before giving it to
+ userspace */
+ fmt->fmt.pix.priv = 0;
return mode; /* used when s_fmt */
}
@@ -1284,9 +1274,6 @@ static void gspca_release(struct v4l2_device *v4l2_device)
struct gspca_dev *gspca_dev =
container_of(v4l2_device, struct gspca_dev, v4l2_dev);
- PDEBUG(D_PROBE, "%s released",
- video_device_node_name(&gspca_dev->vdev));
-
v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
v4l2_device_unregister(&gspca_dev->v4l2_dev);
kfree(gspca_dev->usb_buf);
@@ -1694,8 +1681,6 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- if (!gspca_dev->sd_desc->get_jcomp)
- return -ENOTTY;
gspca_dev->usb_err = 0;
return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
}
@@ -1705,8 +1690,6 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- if (!gspca_dev->sd_desc->set_jcomp)
- return -ENOTTY;
gspca_dev->usb_err = 0;
return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
}
@@ -2290,6 +2273,20 @@ int gspca_dev_probe2(struct usb_interface *intf,
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ if (!gspca_dev->sd_desc->get_chip_ident ||
+ !gspca_dev->sd_desc->get_register)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
+ if (!gspca_dev->sd_desc->get_chip_ident ||
+ !gspca_dev->sd_desc->set_register)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
+#endif
+ if (!gspca_dev->sd_desc->get_jcomp)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
+ if (!gspca_dev->sd_desc->set_jcomp)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
/* init video stuff */
ret = video_register_device(&gspca_dev->vdev,
@@ -2429,7 +2426,6 @@ int gspca_resume(struct usb_interface *intf)
*/
streaming = gspca_dev->streaming;
gspca_dev->streaming = 0;
- v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
if (streaming)
ret = gspca_init_transfer(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 5ab3f7e12760..26b99310d628 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -54,21 +54,13 @@ enum {
#define CAMQUALITY_MIN 0 /* highest cam quality */
#define CAMQUALITY_MAX 97 /* lowest cam quality */
-enum e_ctrl {
- LIGHTFREQ,
- AUTOGAIN,
- RED,
- GREEN,
- BLUE,
- NCTRLS /* number of controls */
-};
-
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
int blocks_left;
const struct v4l2_pix_format *cap_mode;
+ struct v4l2_ctrl *freq;
+ struct v4l2_ctrl *jpegqual;
/* Driver stuff */
u8 type;
u8 quality; /* image quality */
@@ -139,23 +131,21 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
}
}
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 freq_commands[][2] = {
{0x71, 0x80},
{0x70, 0x07}
};
- freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1);
+ freq_commands[0][1] |= val >> 1;
jlj_write2(gspca_dev, freq_commands[0]);
jlj_write2(gspca_dev, freq_commands[1]);
}
-static void setcamquality(struct gspca_dev *gspca_dev)
+static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 quality_commands[][2] = {
{0x71, 0x1E},
{0x70, 0x06}
@@ -163,7 +153,7 @@ static void setcamquality(struct gspca_dev *gspca_dev)
u8 camquality;
/* adapt camera quality from jpeg quality */
- camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX)
+ camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
/ (QUALITY_MAX - QUALITY_MIN);
quality_commands[0][1] += camquality;
@@ -171,130 +161,58 @@ static void setcamquality(struct gspca_dev *gspca_dev)
jlj_write2(gspca_dev, quality_commands[1]);
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 autogain_commands[][2] = {
{0x94, 0x02},
{0xcf, 0x00}
};
- autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4);
+ autogain_commands[1][1] = val << 4;
jlj_write2(gspca_dev, autogain_commands[0]);
jlj_write2(gspca_dev, autogain_commands[1]);
}
-static void setred(struct gspca_dev *gspca_dev)
+static void setred(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 setred_commands[][2] = {
{0x94, 0x02},
{0xe6, 0x00}
};
- setred_commands[1][1] = sd->ctrls[RED].val;
+ setred_commands[1][1] = val;
jlj_write2(gspca_dev, setred_commands[0]);
jlj_write2(gspca_dev, setred_commands[1]);
}
-static void setgreen(struct gspca_dev *gspca_dev)
+static void setgreen(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 setgreen_commands[][2] = {
{0x94, 0x02},
{0xe7, 0x00}
};
- setgreen_commands[1][1] = sd->ctrls[GREEN].val;
+ setgreen_commands[1][1] = val;
jlj_write2(gspca_dev, setgreen_commands[0]);
jlj_write2(gspca_dev, setgreen_commands[1]);
}
-static void setblue(struct gspca_dev *gspca_dev)
+static void setblue(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 setblue_commands[][2] = {
{0x94, 0x02},
{0xe9, 0x00}
};
- setblue_commands[1][1] = sd->ctrls[BLUE].val;
+ setblue_commands[1][1] = val;
jlj_write2(gspca_dev, setblue_commands[0]);
jlj_write2(gspca_dev, setblue_commands[1]);
}
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[LIGHTFREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */
- .maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
- .step = 1,
- .default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
- },
- .set_control = setfreq
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Automatic Gain (and Exposure)",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
-#define AUTOGAIN_DEF 0
- .default_value = AUTOGAIN_DEF,
- },
- .set_control = setautogain
- },
-[RED] = {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "red balance",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
-#define RED_BALANCE_DEF 2
- .default_value = RED_BALANCE_DEF,
- },
- .set_control = setred
- },
-
-[GREEN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "green balance",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
-#define GREEN_BALANCE_DEF 2
- .default_value = GREEN_BALANCE_DEF,
- },
- .set_control = setgreen
- },
-[BLUE] = {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "blue balance",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
-#define BLUE_BALANCE_DEF 2
- .default_value = BLUE_BALANCE_DEF,
- },
- .set_control = setblue
- },
-};
-
static int jlj_start(struct gspca_dev *gspca_dev)
{
int i;
@@ -344,9 +262,9 @@ static int jlj_start(struct gspca_dev *gspca_dev)
if (start_commands[i].ack_wanted)
jlj_read1(gspca_dev, response);
}
- setcamquality(gspca_dev);
+ setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
msleep(2);
- setfreq(gspca_dev);
+ setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
if (gspca_dev->usb_err < 0)
PDEBUG(D_ERR, "Start streaming command failed");
return gspca_dev->usb_err;
@@ -403,7 +321,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
struct sd *dev = (struct sd *) gspca_dev;
dev->type = id->driver_info;
- gspca_dev->cam.ctrls = dev->ctrls;
dev->quality = QUALITY_DEF;
cam->cam_mode = jlj_mode;
@@ -479,25 +396,81 @@ static const struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (menu->id) {
+ 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) {
case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "disable");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
+ setfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setred(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgreen(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setblue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+ setcamquality(gspca_dev, ctrl->val);
break;
}
- return -EINVAL;
+ 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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const struct v4l2_ctrl_config custom_autogain = {
+ .ops = &sd_ctrl_ops,
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain (and Exposure)",
+ .max = 3,
+ .step = 1,
+ .def = 0,
+ };
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
+ v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 3, 1, 2);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
}
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
@@ -505,16 +478,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming) {
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- setcamquality(gspca_dev);
- }
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
return 0;
}
@@ -524,7 +488,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
@@ -546,12 +510,10 @@ static const struct sd_desc sd_desc_sportscam_dv15 = {
.name = MODULE_NAME,
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
- .querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
};
@@ -579,6 +541,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c
index 9c591c7c6f54..cf9d9fca5b84 100644
--- a/drivers/media/video/gspca/jl2005bcd.c
+++ b/drivers/media/video/gspca/jl2005bcd.c
@@ -505,8 +505,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- /* .ctrls = none have been detected */
- /* .nctrls = ARRAY_SIZE(sd_ctrls), */
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -536,6 +534,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c
index e8e8f2fe9166..40ad6687ee5d 100644
--- a/drivers/media/video/gspca/kinect.c
+++ b/drivers/media/video/gspca/kinect.c
@@ -63,12 +63,6 @@ struct sd {
uint8_t ibuf[0x200]; /* input buffer for control commands */
};
-/* V4L2 controls supported by the driver */
-/* controls prototypes here */
-
-static const struct ctrl sd_ctrls[] = {
-};
-
#define MODE_640x480 0x0001
#define MODE_640x488 0x0002
#define MODE_1280x1024 0x0004
@@ -373,15 +367,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
/*
- .querymenu = sd_querymenu,
.get_streamparm = sd_get_streamparm,
.set_streamparm = sd_set_streamparm,
*/
@@ -410,6 +401,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c
index f0c0d74dfe92..bbf91e07e38b 100644
--- a/drivers/media/video/gspca/konica.c
+++ b/drivers/media/video/gspca/konica.c
@@ -50,107 +50,8 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct urb *last_data_urb;
u8 snapshot_pressed;
- u8 brightness;
- u8 contrast;
- u8 saturation;
- u8 whitebal;
- u8 sharpness;
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 9,
- .step = 1,
-#define BRIGHTNESS_DEFAULT 4
- .default_value = BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define SD_CONTRAST 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 9,
- .step = 4,
-#define CONTRAST_DEFAULT 10
- .default_value = CONTRAST_DEFAULT,
- .flags = 0,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define SD_SATURATION 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 9,
- .step = 1,
-#define SATURATION_DEFAULT 4
- .default_value = SATURATION_DEFAULT,
- .flags = 0,
- },
- .set = sd_setsaturation,
- .get = sd_getsaturation,
- },
-#define SD_WHITEBAL 3
- {
- {
- .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "White Balance",
- .minimum = 0,
- .maximum = 33,
- .step = 1,
-#define WHITEBAL_DEFAULT 25
- .default_value = WHITEBAL_DEFAULT,
- .flags = 0,
- },
- .set = sd_setwhitebal,
- .get = sd_getwhitebal,
- },
-#define SD_SHARPNESS 4
- {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 9,
- .step = 1,
-#define SHARPNESS_DEFAULT 4
- .default_value = SHARPNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setsharpness,
- .get = sd_getsharpness,
- },
-};
/* .priv is what goes to register 8 for this mode, known working values:
0x00 -> 176x144, cropped
@@ -202,7 +103,8 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
0,
1000);
if (ret < 0) {
- pr_err("reg_w err %d\n", ret);
+ pr_err("reg_w err writing %02x to %02x: %d\n",
+ value, index, ret);
gspca_dev->usb_err = ret;
}
}
@@ -223,7 +125,7 @@ static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
2,
1000);
if (ret < 0) {
- pr_err("reg_w err %d\n", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
}
}
@@ -242,34 +144,33 @@ static void konica_stream_off(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->cam.cam_mode = vga_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
gspca_dev->cam.no_urb_create = 1;
- sd->brightness = BRIGHTNESS_DEFAULT;
- sd->contrast = CONTRAST_DEFAULT;
- sd->saturation = SATURATION_DEFAULT;
- sd->whitebal = WHITEBAL_DEFAULT;
- sd->sharpness = SHARPNESS_DEFAULT;
-
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- /* HDG not sure if these 2 reads are needed */
- reg_r(gspca_dev, 0, 0x10);
- PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
- gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
- reg_r(gspca_dev, 0, 0x10);
- PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
- gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ int i;
+
+ /*
+ * The konica needs a freaking large time to "boot" (approx 6.5 sec.),
+ * and does not want to be bothered while doing so :|
+ * Register 0x10 counts from 1 - 3, with 3 being "ready"
+ */
+ msleep(6000);
+ for (i = 0; i < 20; i++) {
+ reg_r(gspca_dev, 0, 0x10);
+ if (gspca_dev->usb_buf[0] == 3)
+ break;
+ msleep(100);
+ }
reg_w(gspca_dev, 0, 0x0d);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
@@ -289,12 +190,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
- reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
- reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
- reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
- reg_w(gspca_dev, sd->saturation, SATURATION_REG);
- reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
-
n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
reg_w(gspca_dev, n, 0x08);
@@ -479,125 +374,82 @@ resubmit:
pr_err("usb_submit_urb(status_urb) ret %d\n", st);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming) {
- konica_stream_off(gspca_dev);
- reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
- konica_stream_on(gspca_dev);
- }
-
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- sd->contrast = val;
- if (gspca_dev->streaming) {
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
konica_stream_off(gspca_dev);
- reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+ reg_w(gspca_dev, ctrl->val, BRIGHTNESS_REG);
konica_stream_on(gspca_dev);
- }
-
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
-
- return 0;
-}
-
-static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->saturation = val;
- if (gspca_dev->streaming) {
+ break;
+ case V4L2_CID_CONTRAST:
konica_stream_off(gspca_dev);
- reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+ reg_w(gspca_dev, ctrl->val, CONTRAST_REG);
konica_stream_on(gspca_dev);
- }
- return 0;
-}
-
-static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->saturation;
-
- return 0;
-}
-
-static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->whitebal = val;
- if (gspca_dev->streaming) {
+ break;
+ case V4L2_CID_SATURATION:
konica_stream_off(gspca_dev);
- reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+ reg_w(gspca_dev, ctrl->val, SATURATION_REG);
konica_stream_on(gspca_dev);
- }
- return 0;
-}
-
-static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->whitebal;
-
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming) {
+ break;
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
konica_stream_off(gspca_dev);
- reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+ reg_w(gspca_dev, ctrl->val, WHITEBAL_REG);
konica_stream_on(gspca_dev);
+ break;
+ case V4L2_CID_SHARPNESS:
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, ctrl->val, SHARPNESS_REG);
+ konica_stream_on(gspca_dev);
+ break;
}
- return 0;
+ return gspca_dev->usb_err;
}
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
+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;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 9, 1, 4);
+ /* Needs to be verified */
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 9, 1, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 9, 1, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 33, 1, 25);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 9, 1, 4);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -628,6 +480,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index 0c4493675438..ed22638978ce 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -400,6 +400,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
.disconnect = m5602_disconnect
};
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index ec7b21ee79fb..ff2c5abf115b 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -30,6 +30,8 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
MODULE_LICENSE("GPL");
+#define QUALITY 50
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
@@ -42,13 +44,6 @@ struct sd {
struct v4l2_ctrl *illum_top;
struct v4l2_ctrl *illum_bottom;
};
- struct v4l2_ctrl *jpegqual;
-
- u8 quality;
-#define QUALITY_MIN 40
-#define QUALITY_MAX 70
-#define QUALITY_DEF 50
-
u8 jpeg_hdr[JPEG_HDR_SZ];
};
@@ -194,9 +189,6 @@ static int mars_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_SHARPNESS:
setsharpness(gspca_dev, ctrl->val);
break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
- break;
default:
return -EINVAL;
}
@@ -214,7 +206,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 7);
+ v4l2_ctrl_handler_init(hdl, 6);
sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 30, 1, 15);
sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
@@ -229,9 +221,6 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE;
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
if (hdl->error) {
pr_err("Could not initialize controls\n");
return hdl->error;
@@ -244,13 +233,11 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
- sd->quality = QUALITY_DEF;
return 0;
}
@@ -269,7 +256,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
- jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
+ jpeg_set_qual(sd->jpeg_hdr, QUALITY);
data = gspca_dev->usb_buf;
@@ -411,31 +398,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
- if (ret)
- return ret;
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- return 0;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -445,8 +407,6 @@ static const struct sd_desc sd_desc = {
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index d73e5bd3dbf7..8f4714df5990 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -67,6 +67,7 @@
#define MR97310A_CS_GAIN_MAX 0x7ff
#define MR97310A_CS_GAIN_DEFAULT 0x110
+#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
#define MR97310A_MIN_CLOCKDIV_MIN 3
#define MR97310A_MIN_CLOCKDIV_MAX 8
#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
@@ -84,17 +85,15 @@ MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct { /* exposure/min_clockdiv control cluster */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *min_clockdiv;
+ };
u8 sof_read;
u8 cam_type; /* 0 is CIF and 1 is VGA */
u8 sensor_type; /* We use 0 and 1 here, too. */
u8 do_lcd_stop;
u8 adj_colors;
-
- int brightness;
- u16 exposure;
- u32 gain;
- u8 contrast;
- u8 min_clockdiv;
};
struct sensor_w_data {
@@ -105,132 +104,6 @@ struct sensor_w_data {
};
static void sd_stopN(struct gspca_dev *gspca_dev);
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-/* Separate brightness control description for Argus QuickClix as it has
- * different limits from the other mr97310a cameras, and separate gain
- * control for Sakar CyberPix camera. */
- {
-#define NORM_BRIGHTNESS_IDX 0
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = -254,
- .maximum = 255,
- .step = 1,
- .default_value = MR97310A_BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
-#define ARGUS_QC_BRIGHTNESS_IDX 1
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = MR97310A_BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
-#define EXPOSURE_IDX 2
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = MR97310A_EXPOSURE_MIN,
- .maximum = MR97310A_EXPOSURE_MAX,
- .step = 1,
- .default_value = MR97310A_EXPOSURE_DEFAULT,
- .flags = 0,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
- {
-#define GAIN_IDX 3
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = MR97310A_GAIN_MIN,
- .maximum = MR97310A_GAIN_MAX,
- .step = 1,
- .default_value = MR97310A_GAIN_DEFAULT,
- .flags = 0,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
- {
-#define SAKAR_CS_GAIN_IDX 4
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = MR97310A_CS_GAIN_MIN,
- .maximum = MR97310A_CS_GAIN_MAX,
- .step = 1,
- .default_value = MR97310A_CS_GAIN_DEFAULT,
- .flags = 0,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
- {
-#define CONTRAST_IDX 5
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = MR97310A_CONTRAST_MIN,
- .maximum = MR97310A_CONTRAST_MAX,
- .step = 1,
- .default_value = MR97310A_CONTRAST_DEFAULT,
- .flags = 0,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
-#define MIN_CLOCKDIV_IDX 6
- {
- .id = V4L2_CID_PRIVATE_BASE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Minimum Clock Divider",
- .minimum = MR97310A_MIN_CLOCKDIV_MIN,
- .maximum = MR97310A_MIN_CLOCKDIV_MAX,
- .step = 1,
- .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
- .flags = 0,
- },
- .set = sd_setmin_clockdiv,
- .get = sd_getmin_clockdiv,
- },
-};
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
@@ -481,7 +354,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- int gain_default = MR97310A_GAIN_DEFAULT;
int err_code;
cam = &gspca_dev->cam;
@@ -615,52 +487,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor_type);
}
- /* Setup controls depending on camera type */
- if (sd->cam_type == CAM_TYPE_CIF) {
- /* No brightness for sensor_type 0 */
- if (sd->sensor_type == 0)
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- else
- gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- } else {
- /* All controls need to be disabled if VGA sensor_type is 0 */
- if (sd->sensor_type == 0)
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << EXPOSURE_IDX) |
- (1 << GAIN_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- else if (sd->sensor_type == 2) {
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- gain_default = MR97310A_CS_GAIN_DEFAULT;
- } else if (sd->do_lcd_stop)
- /* Argus QuickClix has different brightness limits */
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- else
- gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- }
-
- sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
- sd->exposure = MR97310A_EXPOSURE_DEFAULT;
- sd->gain = gain_default;
- sd->contrast = MR97310A_CONTRAST_DEFAULT;
- sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
-
return 0;
}
@@ -952,11 +778,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
-
return isoc_enable(gspca_dev);
}
@@ -971,37 +792,25 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
lcd_stop(gspca_dev);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
static const u8 quick_clix_table[] =
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
- /*
- * This control is disabled for CIF type 1 and VGA type 0 cameras.
- * It does not quite act linearly for the Argus QuickClix camera,
- * but it does control brightness. The values are 0 - 15 only, and
- * the table above makes them act consecutively.
- */
- if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
- (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
- return;
-
if (sd->cam_type == CAM_TYPE_VGA) {
sign_reg += 4;
value_reg += 4;
}
/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
- if (sd->brightness > 0) {
+ if (val > 0) {
sensor_write1(gspca_dev, sign_reg, 0x00);
- val = sd->brightness;
} else {
sensor_write1(gspca_dev, sign_reg, 0x01);
- val = (257 - sd->brightness);
+ val = 257 - val;
}
/* Use lookup table for funky Argus QuickClix brightness */
if (sd->do_lcd_stop)
@@ -1010,23 +819,20 @@ static void setbrightness(struct gspca_dev *gspca_dev)
sensor_write1(gspca_dev, value_reg, val);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
{
struct sd *sd = (struct sd *) gspca_dev;
int exposure = MR97310A_EXPOSURE_DEFAULT;
u8 buf[2];
- if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
- return;
-
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
/* This cam does not like exposure settings < 300,
so scale 0 - 4095 to 300 - 4095 */
- exposure = (sd->exposure * 9267) / 10000 + 300;
+ exposure = (expo * 9267) / 10000 + 300;
sensor_write1(gspca_dev, 3, exposure >> 4);
sensor_write1(gspca_dev, 4, exposure & 0x0f);
} else if (sd->sensor_type == 2) {
- exposure = sd->exposure;
+ exposure = expo;
exposure >>= 3;
sensor_write1(gspca_dev, 3, exposure >> 8);
sensor_write1(gspca_dev, 4, exposure & 0xff);
@@ -1038,11 +844,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
Note our 0 - 4095 exposure is mapped to 0 - 511
milliseconds exposure time */
- u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+ u8 clockdiv = (60 * expo + 7999) / 8000;
/* Limit framerate to not exceed usb bandwidth */
- if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
- clockdiv = sd->min_clockdiv;
+ if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
+ clockdiv = min_clockdiv;
else if (clockdiv < 2)
clockdiv = 2;
@@ -1051,7 +857,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
- exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+ exposure = (60 * 511 * expo) / (8000 * clockdiv);
if (exposure > 511)
exposure = 511;
@@ -1065,125 +871,148 @@ static void setexposure(struct gspca_dev *gspca_dev)
}
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 gainreg;
- if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
- (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
- return;
-
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
- sensor_write1(gspca_dev, 0x0e, sd->gain);
+ sensor_write1(gspca_dev, 0x0e, val);
else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
- sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
- sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+ sensor_write1(gspca_dev, gainreg, val >> 8);
+ sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
}
else
- sensor_write1(gspca_dev, 0x10, sd->gain);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
- return;
-
- sensor_write1(gspca_dev, 0x1c, sd->contrast);
-}
-
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
+ sensor_write1(gspca_dev, 0x10, val);
}
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
+ sensor_write1(gspca_dev, 0x1c, val);
}
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
+ if (!gspca_dev->streaming)
+ return 0;
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, sd->exposure->val,
+ sd->min_clockdiv ? sd->min_clockdiv->val : 0);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const struct v4l2_ctrl_config clockdiv = {
+ .ops = &sd_ctrl_ops,
+ .id = MR97310A_CID_CLOCKDIV,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum Clock Divider",
+ .min = MR97310A_MIN_CLOCKDIV_MIN,
+ .max = MR97310A_MIN_CLOCKDIV_MAX,
+ .step = 1,
+ .def = MR97310A_MIN_CLOCKDIV_DEFAULT,
+ };
+ bool has_brightness = false;
+ bool has_argus_brightness = false;
+ bool has_contrast = false;
+ bool has_gain = false;
+ bool has_cs_gain = false;
+ bool has_exposure = false;
+ bool has_clockdiv = false;
- sd->min_clockdiv = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ /* Setup controls depending on camera type */
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ /* No brightness for sensor_type 0 */
+ if (sd->sensor_type == 0)
+ has_exposure = has_gain = has_clockdiv = true;
+ else
+ has_exposure = has_gain = has_brightness = true;
+ } else {
+ /* All controls need to be disabled if VGA sensor_type is 0 */
+ if (sd->sensor_type == 0)
+ ; /* no controls! */
+ else if (sd->sensor_type == 2)
+ has_exposure = has_cs_gain = has_contrast = true;
+ else if (sd->do_lcd_stop)
+ has_exposure = has_gain = has_argus_brightness =
+ has_clockdiv = true;
+ else
+ has_exposure = has_gain = has_brightness =
+ has_clockdiv = true;
+ }
- *val = sd->min_clockdiv;
+ /* Separate brightness control description for Argus QuickClix as it has
+ * different limits from the other mr97310a cameras, and separate gain
+ * control for Sakar CyberPix camera. */
+ /*
+ * This control is disabled for CIF type 1 and VGA type 0 cameras.
+ * It does not quite act linearly for the Argus QuickClix camera,
+ * but it does control brightness. The values are 0 - 15 only, and
+ * the table above makes them act consecutively.
+ */
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -254, 255, 1,
+ MR97310A_BRIGHTNESS_DEFAULT);
+ else if (has_argus_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 15, 1,
+ MR97310A_BRIGHTNESS_DEFAULT);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
+ MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
+ if (has_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
+ 1, MR97310A_GAIN_DEFAULT);
+ else if (has_cs_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
+ MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
+ 1, MR97310A_CS_GAIN_DEFAULT);
+ if (has_exposure)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
+ MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
+ if (has_clockdiv)
+ sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (has_exposure && has_clockdiv)
+ v4l2_ctrl_cluster(2, &sd->exposure);
return 0;
}
@@ -1221,10 +1050,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -1256,6 +1084,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c
index 42e021931e60..44c9964b1b3e 100644
--- a/drivers/media/video/gspca/nw80x.c
+++ b/drivers/media/video/gspca/nw80x.c
@@ -32,22 +32,10 @@ MODULE_LICENSE("GPL");
static int webcam;
-/* controls */
-enum e_ctrl {
- GAIN,
- EXPOSURE,
- AUTOGAIN,
- NCTRLS /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
-
u32 ae_res;
s8 ag_cnt;
#define AG_CNT_START 13
@@ -1667,17 +1655,13 @@ static int swap_bits(int v)
return r;
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val, v[2];
+ u8 v[2];
- val = sd->ctrls[GAIN].val;
switch (sd->webcam) {
case P35u:
- /* Note the control goes from 0-255 not 0-127, but anything
- above 127 just means amplifying noise */
- val >>= 1; /* 0 - 255 -> 0 - 127 */
reg_w(gspca_dev, 0x1026, &val, 1);
break;
case Kr651us:
@@ -1690,13 +1674,11 @@ static void setgain(struct gspca_dev *gspca_dev)
}
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- s16 val;
u8 v[2];
- val = sd->ctrls[EXPOSURE].val;
switch (sd->webcam) {
case P35u:
v[0] = ((9 - val) << 3) | 0x01;
@@ -1713,14 +1695,12 @@ static void setexposure(struct gspca_dev *gspca_dev)
}
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
int w, h;
- if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
- return;
- if (!sd->ctrls[AUTOGAIN].val) {
+ if (!val) {
sd->ag_cnt = -1;
return;
}
@@ -1763,7 +1743,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
if ((unsigned) webcam >= NWEBCAMS)
webcam = 0;
sd->webcam = webcam;
- gspca_dev->cam.ctrls = sd->ctrls;
gspca_dev->cam.needs_full_bandwidth = 1;
sd->ag_cnt = -1;
@@ -1834,33 +1813,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
}
}
- switch (sd->webcam) {
- case P35u:
-/* sd->ctrls[EXPOSURE].max = 9;
- * sd->ctrls[EXPOSURE].def = 9; */
- /* coarse expo auto gain function gain minimum, to avoid
- * a large settings jump the first auto adjustment */
- sd->ctrls[GAIN].def = 255 / 5 * 2;
- break;
- case Cvideopro:
- case DvcV6:
- case Kritter:
- gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AUTOGAIN);
- /* fall thru */
- case Kr651us:
- sd->ctrls[EXPOSURE].max = 315;
- sd->ctrls[EXPOSURE].def = 150;
- break;
- default:
- gspca_dev->ctrl_dis = (1 << GAIN) | (1 << EXPOSURE)
- | (1 << AUTOGAIN);
- break;
- }
-#if AUTOGAIN_DEF
- if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
- gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-#endif
return gspca_dev->usb_err;
}
@@ -1925,9 +1878,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
- setgain(gspca_dev);
- setexposure(gspca_dev);
- setautogain(gspca_dev);
sd->exp_too_high_cnt = 0;
sd->exp_too_low_cnt = 0;
return gspca_dev->usb_err;
@@ -1987,24 +1937,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ctrls[AUTOGAIN].val = val;
- if (val)
- gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
- else
- gspca_dev->ctrl_inac = 0;
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-#define WANT_REGULAR_AUTOGAIN
-#define WANT_COARSE_EXPO_AUTOGAIN
-#include "autogain_functions.h"
-
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2024,62 +1956,100 @@ static void do_autogain(struct gspca_dev *gspca_dev)
switch (sd->webcam) {
case P35u:
- coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
+ gspca_coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
break;
default:
- auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0);
+ gspca_expo_autogain(gspca_dev, luma, 100, 5, 230, 0);
break;
}
}
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 253,
- .step = 1,
- .default_value = 128
- },
- .set_control = setgain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 9,
- .step = 1,
- .default_value = 9
- },
- .set_control = setexposure
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = AUTOGAIN_DEF,
- .flags = V4L2_CTRL_FLAG_UPDATE
- },
- .set = sd_setautogain
- },
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ /* autogain/gain/exposure control cluster */
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->is_new)
+ setautogain(gspca_dev, ctrl->val);
+ if (!ctrl->val) {
+ if (gspca_dev->gain->is_new)
+ setgain(gspca_dev, gspca_dev->gain->val);
+ if (gspca_dev->exposure->is_new)
+ setexposure(gspca_dev,
+ gspca_dev->exposure->val);
+ }
+ break;
+ /* Some webcams only have exposure, so handle that separately from the
+ autogain/gain/exposure cluster in the previous case. */
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, gspca_dev->exposure->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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+ switch (sd->webcam) {
+ case P35u:
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ /* For P35u choose coarse expo auto gain function gain minimum,
+ * to avoid a large settings jump the first auto adjustment */
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 127 / 5 * 2);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 9, 1, 9);
+ break;
+ case Kr651us:
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 253, 1, 128);
+ /* fall through */
+ case Cvideopro:
+ case DvcV6:
+ case Kritter:
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 315, 1, 150);
+ break;
+ default:
+ break;
+ }
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -2117,6 +2087,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 183457c5cfdb..bfc7cefa59f8 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -60,25 +60,20 @@ static int frame_rate;
* are getting "Failed to read sensor ID..." */
static int i2c_detect_tries = 10;
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- EXPOSURE,
- COLORS,
- HFLIP,
- VFLIP,
- AUTOBRIGHT,
- AUTOGAIN,
- FREQ,
- NCTRL /* number of controls */
-};
-
/* ov519 device descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRL];
+ struct v4l2_ctrl *jpegqual;
+ struct v4l2_ctrl *freq;
+ struct { /* h/vflip control cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct { /* autobrightness/brightness control cluster */
+ struct v4l2_ctrl *autobright;
+ struct v4l2_ctrl *brightness;
+ };
u8 packet_nr;
@@ -101,7 +96,6 @@ struct sd {
/* Determined by sensor type */
u8 sif;
- u8 quality;
#define QUALITY_MIN 50
#define QUALITY_MAX 70
#define QUALITY_DEF 50
@@ -145,209 +139,112 @@ enum sensors {
really should move the sensor drivers to v4l2 sub drivers. */
#include "w996Xcf.c"
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setautobright(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setfreq(struct gspca_dev *gspca_dev);
-static void setfreq_i(struct sd *sd);
-
-static const struct ctrl sd_ctrls[] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setbrightness,
+/* table of the disabled controls */
+struct ctrl_valid {
+ int has_brightness:1;
+ int has_contrast:1;
+ int has_exposure:1;
+ int has_autogain:1;
+ int has_sat:1;
+ int has_hvflip:1;
+ int has_autobright:1;
+ int has_freq:1;
+};
+
+static const struct ctrl_valid valid_controls[] = {
+ [SEN_OV2610] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
},
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setcontrast,
+ [SEN_OV2610AE] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
},
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setexposure,
+ [SEN_OV3610] = {
+ /* No controls */
},
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setcolors,
+ [SEN_OV6620] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
},
-/* The flip controls work for sensors ov7660 and ov7670 only */
-[HFLIP] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip,
+ [SEN_OV6630] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
},
-[VFLIP] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vflip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip,
+ [SEN_OV66308AF] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
},
-[AUTOBRIGHT] = {
- {
- .id = V4L2_CID_AUTOBRIGHTNESS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Brightness",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setautobright,
+ [SEN_OV7610] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
},
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = V4L2_CTRL_FLAG_UPDATE
- },
- .set = sd_setautogain,
+ [SEN_OV7620] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
},
-[FREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: no flicker, 1: 50Hz, 2:60Hz, 3: auto */
- .step = 1,
- .default_value = 0,
- },
- .set_control = setfreq,
+ [SEN_OV7620AE] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7640] = {
+ .has_brightness = 1,
+ .has_sat = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7648] = {
+ .has_brightness = 1,
+ .has_sat = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7660] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_hvflip = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7670] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_hvflip = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV76BE] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV8610] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ },
+ [SEN_OV9600] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
},
-};
-
-/* table of the disabled controls */
-static const unsigned ctrl_dis[] = {
-[SEN_OV2610] = ((1 << NCTRL) - 1) /* no control */
- ^ ((1 << EXPOSURE) /* but exposure */
- | (1 << AUTOGAIN)), /* and autogain */
-
-[SEN_OV2610AE] = ((1 << NCTRL) - 1) /* no control */
- ^ ((1 << EXPOSURE) /* but exposure */
- | (1 << AUTOGAIN)), /* and autogain */
-
-[SEN_OV3610] = (1 << NCTRL) - 1, /* no control */
-
-[SEN_OV6620] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV6630] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV66308AF] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7610] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7620] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7620AE] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7640] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << AUTOBRIGHT) |
- (1 << CONTRAST) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7648] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << AUTOBRIGHT) |
- (1 << CONTRAST) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7660] = (1 << AUTOBRIGHT) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV7670] = (1 << COLORS) |
- (1 << AUTOBRIGHT) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV76BE] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN),
-
-[SEN_OV8610] = (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << FREQ),
-[SEN_OV9600] = ((1 << NCTRL) - 1) /* no control */
- ^ ((1 << EXPOSURE) /* but exposure */
- | (1 << AUTOGAIN)), /* and autogain */
-
};
static const struct v4l2_pix_format ov519_vga_mode[] = {
@@ -3306,11 +3203,11 @@ static void ov519_set_fr(struct sd *sd)
ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05);
+ i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
}
/* this function is called at probe time */
@@ -3351,8 +3248,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
}
- gspca_dev->cam.ctrls = sd->ctrls;
- sd->quality = QUALITY_DEF;
sd->frame_rate = 15;
return 0;
@@ -3467,8 +3362,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
break;
}
- gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
-
/* initialize the sensor */
switch (sd->sensor) {
case SEN_OV2610:
@@ -3494,8 +3387,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
break;
case SEN_OV6630:
case SEN_OV66308AF:
- sd->ctrls[CONTRAST].def = 200;
- /* The default is too low for the ov6630 */
write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
break;
default:
@@ -3522,26 +3413,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
sd->gspca_dev.curr_mode = 1; /* 640x480 */
ov519_set_mode(sd);
ov519_set_fr(sd);
- sd->ctrls[COLORS].max = 4; /* 0..4 */
- sd->ctrls[COLORS].val =
- sd->ctrls[COLORS].def = 2;
- setcolors(gspca_dev);
- sd->ctrls[CONTRAST].max = 6; /* 0..6 */
- sd->ctrls[CONTRAST].val =
- sd->ctrls[CONTRAST].def = 3;
- setcontrast(gspca_dev);
- sd->ctrls[BRIGHTNESS].max = 6; /* 0..6 */
- sd->ctrls[BRIGHTNESS].val =
- sd->ctrls[BRIGHTNESS].def = 3;
- setbrightness(gspca_dev);
sd_reset_snapshot(gspca_dev);
ov51x_restart(sd);
ov51x_stop(sd); /* not in win traces */
ov51x_led_control(sd, 0);
break;
case SEN_OV7670:
- sd->ctrls[FREQ].max = 3; /* auto */
- sd->ctrls[FREQ].def = 3;
write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
break;
case SEN_OV8610:
@@ -4177,15 +4054,14 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
}
/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->gspca_dev.streaming)
reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */
i2c_w_mask(sd, OV7670_R1E_MVFP,
- OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val
- | OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val,
+ OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
if (sd->gspca_dev.streaming)
reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */
@@ -4333,23 +4209,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
set_ov_sensor_window(sd);
- if (!(sd->gspca_dev.ctrl_dis & (1 << CONTRAST)))
- setcontrast(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << BRIGHTNESS)))
- setbrightness(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << EXPOSURE)))
- setexposure(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << COLORS)))
- setcolors(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & ((1 << HFLIP) | (1 << VFLIP))))
- sethvflip(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOBRIGHT)))
- setautobright(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOGAIN)))
- setautogain(gspca_dev);
- if (!(sd->gspca_dev.ctrl_dis & (1 << FREQ)))
- setfreq_i(sd);
-
/* Force clear snapshot state in case the snapshot button was
pressed while we weren't streaming */
sd->snapshot_needs_reset = 1;
@@ -4605,10 +4464,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* -- management routines -- */
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
static const struct ov_i2c_regvals brit_7660[][7] = {
{{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
{0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
@@ -4626,7 +4484,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
{0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
};
- val = sd->ctrls[BRIGHTNESS].val;
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -4640,9 +4497,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
break;
case SEN_OV7620:
case SEN_OV7620AE:
- /* 7620 doesn't like manual changes when in auto mode */
- if (!sd->ctrls[AUTOBRIGHT].val)
- i2c_w(sd, OV7610_REG_BRT, val);
+ i2c_w(sd, OV7610_REG_BRT, val);
break;
case SEN_OV7660:
write_i2c_regvals(sd, brit_7660[val],
@@ -4656,10 +4511,9 @@ static void setbrightness(struct gspca_dev *gspca_dev)
}
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
static const struct ov_i2c_regvals contrast_7660[][31] = {
{{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
{0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
@@ -4719,7 +4573,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
{0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
};
- val = sd->ctrls[CONTRAST].val;
switch (sd->sensor) {
case SEN_OV7610:
case SEN_OV6620:
@@ -4760,18 +4613,16 @@ static void setcontrast(struct gspca_dev *gspca_dev)
}
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (!sd->ctrls[AUTOGAIN].val)
- i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val);
+ i2c_w(sd, 0x10, val);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
static const struct ov_i2c_regvals colors_7660[][6] = {
{{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
{0x53, 0x19}, {0x54, 0x23}},
@@ -4785,7 +4636,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
{0x53, 0x66}, {0x54, 0x8e}},
};
- val = sd->ctrls[COLORS].val;
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -4819,34 +4669,18 @@ static void setcolors(struct gspca_dev *gspca_dev)
}
}
-static void setautobright(struct gspca_dev *gspca_dev)
+static void setautobright(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10);
+ i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ctrls[AUTOGAIN].val = val;
- if (val) {
- gspca_dev->ctrl_inac |= (1 << EXPOSURE);
- } else {
- gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
- sd->ctrls[EXPOSURE].val = i2c_r(sd, 0x10);
- }
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static void setfreq_i(struct sd *sd)
+static void setfreq_i(struct sd *sd, s32 val)
{
if (sd->sensor == SEN_OV7660
|| sd->sensor == SEN_OV7670) {
- switch (sd->ctrls[FREQ].val) {
+ switch (val) {
case 0: /* Banding filter disabled */
i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
break;
@@ -4868,7 +4702,7 @@ static void setfreq_i(struct sd *sd)
break;
}
} else {
- switch (sd->ctrls[FREQ].val) {
+ switch (val) {
case 0: /* Banding filter disabled */
i2c_w_mask(sd, 0x2d, 0x00, 0x04);
i2c_w_mask(sd, 0x2a, 0x00, 0x80);
@@ -4900,56 +4734,28 @@ static void setfreq_i(struct sd *sd)
}
}
}
-static void setfreq(struct gspca_dev *gspca_dev)
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- setfreq_i(sd);
+ setfreq_i(sd, val);
/* Ugly but necessary */
if (sd->bridge == BRIDGE_W9968CF)
w9968cf_set_crop_window(sd);
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- case 3:
- if (sd->sensor != SEN_OV7670)
- return -EINVAL;
-
- strcpy((char *) menu->name, "Automatic");
- return 0;
- }
- break;
- }
- return -EINVAL;
-}
-
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge != BRIDGE_W9968CF)
- return -EINVAL;
+ return -ENOTTY;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
V4L2_JPEG_MARKER_DRI;
return 0;
@@ -4961,38 +4767,161 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
if (sd->bridge != BRIDGE_W9968CF)
- return -EINVAL;
+ return -ENOTTY;
+
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ return 0;
+}
- if (gspca_dev->streaming)
- return -EBUSY;
+static int sd_g_volatile_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;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
+ gspca_dev->usb_err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->exposure->val = i2c_r(sd, 0x10);
+ break;
+ }
+ return 0;
+}
+
+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) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOBRIGHTNESS:
+ if (ctrl->is_new)
+ setautobright(gspca_dev, ctrl->val);
+ if (!ctrl->val && sd->brightness->is_new)
+ setbrightness(gspca_dev, sd->brightness->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->is_new)
+ setautogain(gspca_dev, ctrl->val);
+ if (!ctrl->val && gspca_dev->exposure->is_new)
+ setexposure(gspca_dev, gspca_dev->exposure->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ return -EBUSY; /* Should never happen, as we grab the ctrl */
+ }
+ return gspca_dev->usb_err;
+}
- /* Return resulting jcomp params to app */
- sd_get_jcomp(gspca_dev, jcomp);
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .g_volatile_ctrl = sd_g_volatile_ctrl,
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 10);
+ if (valid_controls[sd->sensor].has_brightness)
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0,
+ sd->sensor == SEN_OV7660 ? 6 : 255, 1,
+ sd->sensor == SEN_OV7660 ? 3 : 127);
+ if (valid_controls[sd->sensor].has_contrast) {
+ if (sd->sensor == SEN_OV7660)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 6, 1, 3);
+ else
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1,
+ (sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF) ? 200 : 127);
+ }
+ if (valid_controls[sd->sensor].has_sat)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0,
+ sd->sensor == SEN_OV7660 ? 4 : 255, 1,
+ sd->sensor == SEN_OV7660 ? 2 : 127);
+ if (valid_controls[sd->sensor].has_exposure)
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 255, 1, 127);
+ if (valid_controls[sd->sensor].has_hvflip) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
+ if (valid_controls[sd->sensor].has_autobright)
+ sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
+ if (valid_controls[sd->sensor].has_autogain)
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (valid_controls[sd->sensor].has_freq) {
+ if (sd->sensor == SEN_OV7670)
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+ else
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+ }
+ if (sd->bridge == BRIDGE_W9968CF)
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
+ if (sd->autobright)
+ v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
+ if (sd->hflip)
+ v4l2_ctrl_cluster(2, &sd->hflip);
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_reset_snapshot,
- .querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -5052,6 +4981,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 80c81dd6d68b..bb09d7884b89 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -35,6 +35,7 @@
#include "gspca.h"
#include <linux/fixp-arith.h>
+#include <media/v4l2-ctrls.h>
#define OV534_REG_ADDRESS 0xf1 /* sensor address */
#define OV534_REG_SUBADDR 0xf2
@@ -53,29 +54,28 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- HUE,
- SATURATION,
- BRIGHTNESS,
- CONTRAST,
- GAIN,
- EXPOSURE,
- AGC,
- AWB,
- AEC,
- SHARPNESS,
- HFLIP,
- VFLIP,
- LIGHTFREQ,
- NCTRLS /* number of controls */
-};
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *hue;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct { /* gain control cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *autowhitebalance;
+ struct { /* exposure control cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *plfreq;
__u32 last_pts;
u16 last_fid;
@@ -89,181 +89,9 @@ enum sensors {
NSENSORS
};
-/* V4L2 controls supported by the driver */
-static void sethue(struct gspca_dev *gspca_dev);
-static void setsaturation(struct gspca_dev *gspca_dev);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setagc(struct gspca_dev *gspca_dev);
-static void setawb(struct gspca_dev *gspca_dev);
-static void setaec(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
static int sd_start(struct gspca_dev *gspca_dev);
static void sd_stopN(struct gspca_dev *gspca_dev);
-static const struct ctrl sd_ctrls[] = {
-[HUE] = {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -90,
- .maximum = 90,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethue
- },
-[SATURATION] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64,
- },
- .set_control = setsaturation
- },
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 0,
- },
- .set_control = setbrightness
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 32,
- },
- .set_control = setcontrast
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Main Gain",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- .default_value = 20,
- },
- .set_control = setgain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 120,
- },
- .set_control = setexposure
- },
-[AGC] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setagc
- },
-[AWB] = {
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setawb
- },
-[AEC] = {
- {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setaec
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- .default_value = 0,
- },
- .set_control = setsharpness
- },
-[HFLIP] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "HFlip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip
- },
-[VFLIP] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "VFlip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip
- },
-[LIGHTFREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light Frequency Filter",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = setlightfreq
- },
-};
static const struct v4l2_pix_format ov772x_mode[] = {
{320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
@@ -972,12 +800,10 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
}
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
- val = sd->ctrls[HUE].val;
if (sd->sensor == SENSOR_OV767x) {
/* TBD */
} else {
@@ -1014,12 +840,10 @@ static void sethue(struct gspca_dev *gspca_dev)
}
}
-static void setsaturation(struct gspca_dev *gspca_dev)
+static void setsaturation(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
- val = sd->ctrls[SATURATION].val;
if (sd->sensor == SENSOR_OV767x) {
int i;
static u8 color_tb[][6] = {
@@ -1040,12 +864,10 @@ static void setsaturation(struct gspca_dev *gspca_dev)
}
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- int val;
- val = sd->ctrls[BRIGHTNESS].val;
if (sd->sensor == SENSOR_OV767x) {
if (val < 0)
val = 0x80 - val;
@@ -1055,27 +877,18 @@ static void setbrightness(struct gspca_dev *gspca_dev)
}
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
- val = sd->ctrls[CONTRAST].val;
if (sd->sensor == SENSOR_OV767x)
sccb_reg_write(gspca_dev, 0x56, val); /* contras */
else
sccb_reg_write(gspca_dev, 0x9c, val);
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
-
- if (sd->ctrls[AGC].val)
- return;
-
- val = sd->ctrls[GAIN].val;
switch (val & 0x30) {
case 0x00:
val &= 0x0f;
@@ -1097,15 +910,15 @@ static void setgain(struct gspca_dev *gspca_dev)
sccb_reg_write(gspca_dev, 0x00, val);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static s32 getgain(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
+ return sccb_reg_read(gspca_dev, 0x00);
+}
- if (sd->ctrls[AEC].val)
- return;
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
- val = sd->ctrls[EXPOSURE].val;
if (sd->sensor == SENSOR_OV767x) {
/* set only aec[9:2] */
@@ -1123,11 +936,23 @@ static void setexposure(struct gspca_dev *gspca_dev)
}
}
-static void setagc(struct gspca_dev *gspca_dev)
+static s32 getexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->ctrls[AGC].val) {
+ if (sd->sensor == SENSOR_OV767x) {
+ /* get only aec[9:2] */
+ return sccb_reg_read(gspca_dev, 0x10); /* aech */
+ } else {
+ u8 hi = sccb_reg_read(gspca_dev, 0x08);
+ u8 lo = sccb_reg_read(gspca_dev, 0x10);
+ return (hi << 8 | lo) >> 1;
+ }
+}
+
+static void setagc(struct gspca_dev *gspca_dev, s32 val)
+{
+ if (val) {
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) | 0x04);
sccb_reg_write(gspca_dev, 0x64,
@@ -1137,16 +962,14 @@ static void setagc(struct gspca_dev *gspca_dev)
sccb_reg_read(gspca_dev, 0x13) & ~0x04);
sccb_reg_write(gspca_dev, 0x64,
sccb_reg_read(gspca_dev, 0x64) & ~0x03);
-
- setgain(gspca_dev);
}
}
-static void setawb(struct gspca_dev *gspca_dev)
+static void setawb(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->ctrls[AWB].val) {
+ if (val) {
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) | 0x02);
if (sd->sensor == SENSOR_OV772x)
@@ -1161,7 +984,7 @@ static void setawb(struct gspca_dev *gspca_dev)
}
}
-static void setaec(struct gspca_dev *gspca_dev)
+static void setaec(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data;
@@ -1169,31 +992,25 @@ static void setaec(struct gspca_dev *gspca_dev)
data = sd->sensor == SENSOR_OV767x ?
0x05 : /* agc + aec */
0x01; /* agc */
- if (sd->ctrls[AEC].val)
+ switch (val) {
+ case V4L2_EXPOSURE_AUTO:
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) | data);
- else {
+ break;
+ case V4L2_EXPOSURE_MANUAL:
sccb_reg_write(gspca_dev, 0x13,
sccb_reg_read(gspca_dev, 0x13) & ~data);
- if (sd->sensor == SENSOR_OV767x)
- sd->ctrls[EXPOSURE].val =
- sccb_reg_read(gspca_dev, 10); /* aech */
- else
- setexposure(gspca_dev);
+ break;
}
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
-
- val = sd->ctrls[SHARPNESS].val;
sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */
sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */
}
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
@@ -1201,28 +1018,27 @@ static void sethvflip(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_OV767x) {
val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */
val &= ~0x30;
- if (sd->ctrls[HFLIP].val)
+ if (hflip)
val |= 0x20;
- if (sd->ctrls[VFLIP].val)
+ if (vflip)
val |= 0x10;
sccb_reg_write(gspca_dev, 0x1e, val);
} else {
val = sccb_reg_read(gspca_dev, 0x0c);
val &= ~0xc0;
- if (sd->ctrls[HFLIP].val == 0)
+ if (hflip == 0)
val |= 0x40;
- if (sd->ctrls[VFLIP].val == 0)
+ if (vflip == 0)
val |= 0x80;
sccb_reg_write(gspca_dev, 0x0c, val);
}
}
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
- val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
+ val = val ? 0x9e : 0x00;
if (sd->sensor == SENSOR_OV767x) {
sccb_reg_write(gspca_dev, 0x2a, 0x00);
if (val)
@@ -1241,8 +1057,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
- cam->ctrls = sd->ctrls;
-
cam->cam_mode = ov772x_mode;
cam->nmodes = ARRAY_SIZE(ov772x_mode);
@@ -1251,6 +1065,195 @@ static int sd_config(struct gspca_dev *gspca_dev,
return 0;
}
+static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val && sd->gain && gspca_dev->streaming)
+ sd->gain->val = getgain(gspca_dev);
+ return gspca_dev->usb_err;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure &&
+ gspca_dev->streaming)
+ sd->exposure->val = getexposure(gspca_dev);
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int ov534_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ gspca_dev->usb_err = 0;
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HUE:
+ sethue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setsaturation(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* case V4L2_CID_GAIN: */
+ setagc(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && !ctrl->val && sd->gain)
+ setgain(gspca_dev, sd->gain->val);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ setawb(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ /* case V4L2_CID_EXPOSURE: */
+ setaec(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL &&
+ sd->exposure)
+ setexposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+ break;
+ case V4L2_CID_VFLIP:
+ sethvflip(gspca_dev, sd->hflip->val, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops ov534_ctrl_ops = {
+ .g_volatile_ctrl = ov534_g_volatile_ctrl,
+ .s_ctrl = ov534_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
+ /* parameters with different values between the supported sensors */
+ int saturation_min;
+ int saturation_max;
+ int saturation_def;
+ int brightness_min;
+ int brightness_max;
+ int brightness_def;
+ int contrast_max;
+ int contrast_def;
+ int exposure_min;
+ int exposure_max;
+ int exposure_def;
+ int hflip_def;
+
+ if (sd->sensor == SENSOR_OV767x) {
+ saturation_min = 0,
+ saturation_max = 6,
+ saturation_def = 3,
+ brightness_min = -127;
+ brightness_max = 127;
+ brightness_def = 0;
+ contrast_max = 0x80;
+ contrast_def = 0x40;
+ exposure_min = 0x08;
+ exposure_max = 0x60;
+ exposure_def = 0x13;
+ hflip_def = 1;
+ } else {
+ saturation_min = 0,
+ saturation_max = 255,
+ saturation_def = 64,
+ brightness_min = 0;
+ brightness_max = 255;
+ brightness_def = 0;
+ contrast_max = 255;
+ contrast_def = 32;
+ exposure_min = 0;
+ exposure_max = 255;
+ exposure_def = 120;
+ hflip_def = 0;
+ }
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+
+ v4l2_ctrl_handler_init(hdl, 13);
+
+ if (sd->sensor == SENSOR_OV772x)
+ sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_HUE, -90, 90, 1, 0);
+
+ sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_SATURATION, saturation_min, saturation_max, 1,
+ saturation_def);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1,
+ brightness_def);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def);
+
+ if (sd->sensor == SENSOR_OV772x) {
+ sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63, 1, 20);
+ }
+
+ sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+ sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1,
+ exposure_def);
+
+ sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+ if (sd->sensor == SENSOR_OV772x)
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 63, 1, 0);
+
+ sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, hflip_def);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ if (sd->sensor == SENSOR_OV772x)
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+
+ v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL,
+ true);
+
+ return 0;
+}
+
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
@@ -1286,24 +1289,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
if ((sensor_id & 0xfff0) == 0x7670) {
sd->sensor = SENSOR_OV767x;
- gspca_dev->ctrl_dis = (1 << HUE) |
- (1 << GAIN) |
- (1 << AGC) |
- (1 << SHARPNESS); /* auto */
- sd->ctrls[SATURATION].min = 0,
- sd->ctrls[SATURATION].max = 6,
- sd->ctrls[SATURATION].def = 3,
- sd->ctrls[BRIGHTNESS].min = -127;
- sd->ctrls[BRIGHTNESS].max = 127;
- sd->ctrls[BRIGHTNESS].def = 0;
- sd->ctrls[CONTRAST].max = 0x80;
- sd->ctrls[CONTRAST].def = 0x40;
- sd->ctrls[EXPOSURE].min = 0x08;
- sd->ctrls[EXPOSURE].max = 0x60;
- sd->ctrls[EXPOSURE].def = 0x13;
- sd->ctrls[SHARPNESS].max = 9;
- sd->ctrls[SHARPNESS].def = 4;
- sd->ctrls[HFLIP].def = 1;
gspca_dev->cam.cam_mode = ov767x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
} else {
@@ -1366,22 +1351,23 @@ static int sd_start(struct gspca_dev *gspca_dev)
set_frame_rate(gspca_dev);
- if (!(gspca_dev->ctrl_dis & (1 << HUE)))
- sethue(gspca_dev);
- setsaturation(gspca_dev);
- if (!(gspca_dev->ctrl_dis & (1 << AGC)))
- setagc(gspca_dev);
- setawb(gspca_dev);
- setaec(gspca_dev);
- if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
- setgain(gspca_dev);
- setexposure(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
- setsharpness(gspca_dev);
- sethvflip(gspca_dev);
- setlightfreq(gspca_dev);
+ if (sd->hue)
+ sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue));
+ setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation));
+ if (sd->autogain)
+ setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+ setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance));
+ setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure));
+ if (sd->gain)
+ setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+ setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness));
+ setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+ if (sd->sharpness)
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+ sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+ v4l2_ctrl_g_ctrl(sd->vflip));
+ setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
ov534_set_led(gspca_dev, 1);
ov534_reg_write(gspca_dev, 0xe0, 0x00);
@@ -1483,25 +1469,6 @@ scan_next:
} while (remaining_len > 0);
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "Disabled");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- }
- break;
- }
-
- return -EINVAL;
-}
-
/* get stream parameters (framerate) */
static void sd_get_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_streamparm *parm)
@@ -1536,14 +1503,12 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
.get_streamparm = sd_get_streamparm,
.set_streamparm = sd_set_streamparm,
};
@@ -1572,6 +1537,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index 1fd41f0d2e95..c4cd028fe0b4 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -47,22 +47,9 @@ MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- AUTOGAIN,
- EXPOSURE,
- SHARPNESS,
- SATUR,
- LIGHTFREQ,
- NCTRLS /* number of controls */
-};
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
__u32 last_pts;
u8 last_fid;
@@ -75,103 +62,6 @@ enum sensors {
NSENSORS
};
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setautogain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void setsatur(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 7
- },
- .set_control = setbrightness
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 3
- },
- .set_control = setcontrast
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Autogain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set_control = setautogain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0
- },
- .set_control = setexposure
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = -1, /* -1 = auto */
- .maximum = 4,
- .step = 1,
- .default_value = -1
- },
- .set_control = setsharpness
- },
-[SATUR] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 4,
- .step = 1,
- .default_value = 2
- },
- .set_control = setsatur
- },
-[LIGHTFREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
- .default_value = 0
- },
- .set_control = setlightfreq
- },
-};
-
static const struct v4l2_pix_format ov965x_mode[] = {
#define QVGA_MODE 0
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -1104,16 +994,14 @@ static void set_led(struct gspca_dev *gspca_dev, int status)
}
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
s8 sval;
- if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
- return;
if (sd->sensor == SENSOR_OV562x) {
- sval = sd->ctrls[BRIGHTNESS].val;
+ sval = brightness;
val = 0x76;
val += sval;
sccb_write(gspca_dev, 0x24, val);
@@ -1128,7 +1016,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
val = 0xe6;
sccb_write(gspca_dev, 0x26, val);
} else {
- val = sd->ctrls[BRIGHTNESS].val;
+ val = brightness;
if (val < 8)
val = 15 - val; /* f .. 8 */
else
@@ -1138,43 +1026,32 @@ static void setbrightness(struct gspca_dev *gspca_dev)
}
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << CONTRAST))
- return;
sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
- sd->ctrls[CONTRAST].val << 4);
+ val << 4);
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 autogain)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
- return;
/*fixme: should adjust agc/awb/aec by different controls */
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->ctrls[AUTOGAIN].val)
+ if (autogain)
val |= 0x05; /* agc & aec */
else
val &= 0xfa;
sccb_write(gspca_dev, 0x13, val);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 exposure)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+ u8 val;
- if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
- return;
- sccb_write(gspca_dev, 0x10, /* aec[9:2] */
- expo[sd->ctrls[EXPOSURE].val]);
+ sccb_write(gspca_dev, 0x10, expo[exposure]); /* aec[9:2] */
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
@@ -1185,14 +1062,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- s8 val;
-
- if (gspca_dev->ctrl_dis & (1 << SHARPNESS))
- return;
- val = sd->ctrls[SHARPNESS].val;
if (val < 0) { /* auto */
val = sccb_read(gspca_dev, 0x42); /* com17 */
sccb_write(gspca_dev, 0xff, 0x00);
@@ -1209,9 +1080,8 @@ static void setsharpness(struct gspca_dev *gspca_dev)
sccb_write(gspca_dev, 0x42, val & 0xbf);
}
-static void setsatur(struct gspca_dev *gspca_dev)
+static void setsatur(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 val1, val2, val3;
static const u8 matrix[5][2] = {
{0x14, 0x38},
@@ -1221,10 +1091,8 @@ static void setsatur(struct gspca_dev *gspca_dev)
{0x48, 0x90}
};
- if (gspca_dev->ctrl_dis & (1 << SATUR))
- return;
- val1 = matrix[sd->ctrls[SATUR].val][0];
- val2 = matrix[sd->ctrls[SATUR].val][1];
+ val1 = matrix[val][0];
+ val2 = matrix[val][1];
val3 = val1 + val2;
sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
sccb_write(gspca_dev, 0x50, val3);
@@ -1239,16 +1107,13 @@ static void setsatur(struct gspca_dev *gspca_dev)
sccb_write(gspca_dev, 0x41, val1);
}
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 freq)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 val;
- if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ))
- return;
val = sccb_read(gspca_dev, 0x13); /* com8 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->ctrls[LIGHTFREQ].val == 0) {
+ if (freq == 0) {
sccb_write(gspca_dev, 0x13, val & 0xdf);
return;
}
@@ -1256,7 +1121,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
val = sccb_read(gspca_dev, 0x42); /* com17 */
sccb_write(gspca_dev, 0xff, 0x00);
- if (sd->ctrls[LIGHTFREQ].val == 1)
+ if (freq == 1)
val |= 0x01;
else
val &= 0xfe;
@@ -1267,13 +1132,6 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- gspca_dev->cam.ctrls = sd->ctrls;
-
-#if AUTOGAIN_DEF != 0
- gspca_dev->ctrl_inac |= (1 << EXPOSURE);
-#endif
return 0;
}
@@ -1330,9 +1188,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
gspca_dev->cam.cam_mode = ov971x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
- /* no control yet */
- gspca_dev->ctrl_dis = (1 << NCTRLS) - 1;
-
gspca_dev->cam.bulk = 1;
gspca_dev->cam.bulk_size = 16384;
gspca_dev->cam.bulk_nurbs = 2;
@@ -1358,16 +1213,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x56, 0x17);
} else if ((sensor_id & 0xfff0) == 0x5620) {
sd->sensor = SENSOR_OV562x;
- gspca_dev->ctrl_dis = (1 << CONTRAST) |
- (1 << AUTOGAIN) |
- (1 << EXPOSURE) |
- (1 << SHARPNESS) |
- (1 << SATUR) |
- (1 << LIGHTFREQ);
-
- sd->ctrls[BRIGHTNESS].min = -90;
- sd->ctrls[BRIGHTNESS].max = 90;
- sd->ctrls[BRIGHTNESS].def = 0;
gspca_dev->cam.cam_mode = ov562x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
@@ -1390,10 +1235,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_OV971x)
return gspca_dev->usb_err;
- else if (sd->sensor == SENSOR_OV562x) {
- setbrightness(gspca_dev);
+ if (sd->sensor == SENSOR_OV562x)
return gspca_dev->usb_err;
- }
+
switch (gspca_dev->curr_mode) {
case QVGA_MODE: /* 320x240 */
sccb_w_array(gspca_dev, ov965x_start_1_vga,
@@ -1437,13 +1281,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
ARRAY_SIZE(ov965x_start_2_sxga));
break;
}
- setlightfreq(gspca_dev);
- setautogain(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setexposure(gspca_dev);
- setsharpness(gspca_dev);
- setsatur(gspca_dev);
reg_w(gspca_dev, 0xe0, 0x00);
reg_w(gspca_dev, 0xe0, 0x00);
@@ -1541,38 +1378,94 @@ scan_next:
} while (remaining_len > 0);
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (menu->id) {
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setsatur(gspca_dev, ctrl->val);
+ break;
case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
+ setlightfreq(gspca_dev, ctrl->val);
break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->is_new)
+ setautogain(gspca_dev, ctrl->val);
+ if (!ctrl->val && gspca_dev->exposure->is_new)
+ setexposure(gspca_dev, gspca_dev->exposure->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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ if (sd->sensor == SENSOR_OV971x)
+ return 0;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 7);
+ if (sd->sensor == SENSOR_OV562x) {
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -90, 90, 1, 0);
+ } else {
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 15, 1, 7);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 15, 1, 3);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 4, 1, 2);
+ /* -1 = auto */
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, -1, 4, 1, -1);
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 3, 1, 0);
+ v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
}
- return -EINVAL;
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
};
/* -- module initialisation -- */
@@ -1600,6 +1493,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index fa661c6d6d55..d236d1791f78 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -462,6 +462,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index a0369a58c4bb..4877f7ab3d59 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -84,31 +84,31 @@
/* Include pac common sof detection functions */
#include "pac_common.h"
+#define PAC7302_GAIN_DEFAULT 15
+#define PAC7302_GAIN_KNEE 42
+#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
+#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
+
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
"Thomas Kaiser thomas@kaiser-linux.li");
MODULE_DESCRIPTION("Pixart PAC7302");
MODULE_LICENSE("GPL");
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- COLORS,
- WHITE_BALANCE,
- RED_BALANCE,
- BLUE_BALANCE,
- GAIN,
- AUTOGAIN,
- EXPOSURE,
- VFLIP,
- HFLIP,
- NCTRLS /* number of controls */
-};
-
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
-
+ struct { /* brightness / contrast cluster */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ };
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *white_balance;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ struct { /* flip cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
u8 flags;
#define FL_HFLIP 0x01 /* mirrored by default */
#define FL_VFLIP 0x02 /* vertical flipped by default */
@@ -119,160 +119,6 @@ struct sd {
atomic_t avg_lum;
};
-/* V4L2 controls supported by the driver */
-static void setbrightcont(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setwhitebalance(struct gspca_dev *gspca_dev);
-static void setredbalance(struct gspca_dev *gspca_dev);
-static void setbluebalance(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setautogain(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
-#define BRIGHTNESS_MAX 0x20
- .maximum = BRIGHTNESS_MAX,
- .step = 1,
- .default_value = 0x10,
- },
- .set_control = setbrightcont
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
-#define CONTRAST_MAX 255
- .maximum = CONTRAST_MAX,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setbrightcont
- },
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
-#define COLOR_MAX 255
- .maximum = COLOR_MAX,
- .step = 1,
- .default_value = 127
- },
- .set_control = setcolors
- },
-[WHITE_BALANCE] = {
- {
- .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "White Balance",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 4,
- },
- .set_control = setwhitebalance
- },
-[RED_BALANCE] = {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setredbalance
- },
-[BLUE_BALANCE] = {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setbluebalance
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 62,
- .step = 1,
-#define GAIN_DEF 15
-#define GAIN_KNEE 46
- .default_value = GAIN_DEF,
- },
- .set_control = setgain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 1023,
- .step = 1,
-#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */
-#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
- .default_value = EXPOSURE_DEF,
- },
- .set_control = setexposure
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set_control = setautogain,
- },
-[HFLIP] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip,
- },
-[VFLIP] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vflip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
.bytesperline = 640,
@@ -516,8 +362,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = vga_mode; /* only 640x480 */
cam->nmodes = ARRAY_SIZE(vga_mode);
- gspca_dev->cam.ctrls = sd->ctrls;
-
sd->flags = id->driver_info;
return 0;
}
@@ -536,9 +380,9 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
for (i = 0; i < 10; i++) {
v = max[i];
- v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
- * 150 / BRIGHTNESS_MAX; /* 200 ? */
- v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
+ v += (sd->brightness->val - sd->brightness->maximum)
+ * 150 / sd->brightness->maximum; /* 200 ? */
+ v -= delta[i] * sd->contrast->val / sd->contrast->maximum;
if (v < 0)
v = 0;
else if (v > 0xff)
@@ -561,7 +405,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x11, 0x01);
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
for (i = 0; i < 9; i++) {
- v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
+ v = a[i] * sd->saturation->val / sd->saturation->maximum;
+ v += b[i];
reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
}
@@ -573,7 +418,7 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
+ reg_w(gspca_dev, 0xc6, sd->white_balance->val);
reg_w(gspca_dev, 0xdc, 0x01);
}
@@ -583,7 +428,7 @@ static void setredbalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
+ reg_w(gspca_dev, 0xc5, sd->red_balance->val);
reg_w(gspca_dev, 0xdc, 0x01);
}
@@ -593,22 +438,21 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
+ reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
reg_w(gspca_dev, 0xdc, 0x01);
}
static void setgain(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 reg10, reg12;
- if (sd->ctrls[GAIN].val < 32) {
- reg10 = sd->ctrls[GAIN].val;
+ if (gspca_dev->gain->val < 32) {
+ reg10 = gspca_dev->gain->val;
reg12 = 0;
} else {
reg10 = 31;
- reg12 = sd->ctrls[GAIN].val - 31;
+ reg12 = gspca_dev->gain->val - 31;
}
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
@@ -621,7 +465,6 @@ static void setgain(struct gspca_dev *gspca_dev)
static void setexposure(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 clockdiv;
u16 exposure;
@@ -630,7 +473,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
* no fps according to the formula: 90 / reg. sd->exposure is the
* desired exposure time in 0.5 ms.
*/
- clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
+ clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
/*
* Note clockdiv = 3 also works, but when running at 30 fps, depending
@@ -655,7 +498,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
* frame exposure time in ms = 1000 * clockdiv / 90 ->
* exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
*/
- exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
+ exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
/* 0 = use full frametime, 448 = no exposure, reverse it */
exposure = 448 - exposure;
@@ -668,37 +511,15 @@ static void setexposure(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x11, 0x01);
}
-static void setautogain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /*
- * When switching to autogain set defaults to make sure
- * we are on a valid point of the autogain gain /
- * exposure knee graph, and give this change time to
- * take effect before doing autogain.
- */
- if (sd->ctrls[AUTOGAIN].val) {
- sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
- sd->ctrls[GAIN].val = GAIN_DEF;
- sd->autogain_ignore_frames =
- PAC_AUTOGAIN_IGNORE_FRAMES;
- } else {
- sd->autogain_ignore_frames = -1;
- }
- setexposure(gspca_dev);
- setgain(gspca_dev);
-}
-
static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data, hflip, vflip;
- hflip = sd->ctrls[HFLIP].val;
+ hflip = sd->hflip->val;
if (sd->flags & FL_HFLIP)
hflip = !hflip;
- vflip = sd->ctrls[VFLIP].val;
+ vflip = sd->vflip->val;
if (sd->flags & FL_VFLIP)
vflip = !vflip;
@@ -717,6 +538,112 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
+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 (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT;
+ gspca_dev->gain->val = PAC7302_GAIN_DEFAULT;
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightcont(gspca_dev);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev);
+ break;
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ setwhitebalance(gspca_dev);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setredbalance(gspca_dev);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setbluebalance(gspca_dev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+ setexposure(gspca_dev);
+ if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+ setgain(gspca_dev);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 11);
+
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+
+ sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 255, 1, 4);
+ sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+ sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 1023, 1,
+ PAC7302_EXPOSURE_DEFAULT);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 62, 1,
+ PAC7302_GAIN_DEFAULT);
+
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ v4l2_ctrl_cluster(2, &sd->brightness);
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ return 0;
+}
+
+/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -728,11 +655,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
setwhitebalance(gspca_dev);
setredbalance(gspca_dev);
setbluebalance(gspca_dev);
- setautogain(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
sethvflip(gspca_dev);
sd->sof_read = 0;
- atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
+ sd->autogain_ignore_frames = 0;
+ atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
/* start stream */
reg_w(gspca_dev, 0xff, 0x01);
@@ -758,9 +687,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x78, 0x40);
}
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -774,11 +700,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (sd->autogain_ignore_frames > 0) {
sd->autogain_ignore_frames--;
} else {
- desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
+ desired_lum = 270 + sd->brightness->val;
- auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
- deadzone, GAIN_KNEE, EXPOSURE_KNEE);
- sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
+ deadzone, PAC7302_GAIN_KNEE,
+ PAC7302_EXPOSURE_KNEE))
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
}
}
@@ -944,10 +872,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description for pac7302 */
static const struct sd_desc sd_desc = {
.name = KBUILD_MODNAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
@@ -998,6 +925,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index 115da169f32a..ba3558d3f017 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -694,6 +694,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c
index bb70092c2229..a33cb78a839c 100644
--- a/drivers/media/video/gspca/se401.c
+++ b/drivers/media/video/gspca/se401.c
@@ -45,15 +45,6 @@ MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Endpoints se401");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- GAIN,
- EXPOSURE,
- FREQ,
- NCTRL /* number of controls */
-};
-
/* exposure change state machine states */
enum {
EXPO_CHANGED,
@@ -64,7 +55,11 @@ enum {
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRL];
+ struct { /* exposure/freq control cluster */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *freq;
+ };
+ bool has_brightness;
struct v4l2_pix_format fmts[MAX_MODES];
int pixels_read;
int packet_read;
@@ -77,60 +72,6 @@ struct sd {
int expo_change_state;
};
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRL] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 15,
- },
- .set_control = setbrightness
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 50, /* Really 63 but > 50 is not pretty */
- .step = 1,
- .default_value = 25,
- },
- .set_control = setgain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 32767,
- .step = 1,
- .default_value = 15000,
- },
- .set_control = setexposure
- },
-[FREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2,
- .step = 1,
- .default_value = 0,
- },
- .set_control = setexposure
- },
-};
static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
int silent)
@@ -224,22 +165,15 @@ static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
- return;
-
/* HDG: this does not seem to do anything on my cam */
- se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
- sd->ctrls[BRIGHTNESS].val, 0);
+ se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0);
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 gain = 63 - sd->ctrls[GAIN].val;
+ u16 gain = 63 - val;
/* red color gain */
se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
@@ -249,10 +183,10 @@ static void setgain(struct gspca_dev *gspca_dev)
se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq)
{
struct sd *sd = (struct sd *) gspca_dev;
- int integration = sd->ctrls[EXPOSURE].val << 6;
+ int integration = val << 6;
u8 expose_h, expose_m, expose_l;
/* Do this before the set_feature calls, for proper timing wrt
@@ -262,9 +196,9 @@ static void setexposure(struct gspca_dev *gspca_dev)
through so be it */
sd->expo_change_state = EXPO_CHANGED;
- if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+ if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
integration = integration - integration % 106667;
- if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+ if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
integration = integration - integration % 88889;
expose_h = (integration >> 16);
@@ -375,15 +309,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->bulk = 1;
cam->bulk_size = BULK_SIZE;
cam->bulk_nurbs = 4;
- cam->ctrls = sd->ctrls;
sd->resetlevel = 0x2d; /* Set initial resetlevel */
/* See if the camera supports brightness */
se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
- if (gspca_dev->usb_err) {
- gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
- gspca_dev->usb_err = 0;
- }
+ sd->has_brightness = !!gspca_dev->usb_err;
+ gspca_dev->usb_err = 0;
return 0;
}
@@ -442,9 +373,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
- setbrightness(gspca_dev);
- setgain(gspca_dev);
- setexposure(gspca_dev);
se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
sd->packet_read = 0;
@@ -666,27 +594,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
sd_pkt_scan_janggu(gspca_dev, data, len);
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
- break;
- }
- return -EINVAL;
-}
-
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
{
@@ -714,19 +621,73 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
}
#endif
+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) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val, sd->freq->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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ if (sd->has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 15);
+ /* max is really 63 but > 50 is not pretty */
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 50, 1, 25);
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 32767, 1, 15000);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(2, &sd->exposure);
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
.dq_callback = sd_dq_callback,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.int_pkt_scan = sd_int_pkt_scan,
#endif
@@ -769,6 +730,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
.pre_reset = sd_pre_reset,
.post_reset = sd_post_reset,
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
index 478533cb1152..03fa3fd940b4 100644
--- a/drivers/media/video/gspca/sn9c2028.c
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -40,10 +40,6 @@ struct init_command {
unsigned char to_read; /* length to read. 0 means no reply requested */
};
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
/* How to change the resolution of any of the VGA cams is unknown */
static const struct v4l2_pix_format vga_mode[] = {
{640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
@@ -695,8 +691,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -734,6 +728,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index e2bdf8f632f4..fd1f8d2d3b0b 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -56,26 +56,16 @@ MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- GAIN,
- EXPOSURE,
- AUTOGAIN,
- FREQ,
- NCTRLS /* number of controls */
-};
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *plfreq;
atomic_t avg_lum;
int prev_avg_lum;
- int exp_too_low_cnt;
- int exp_too_high_cnt;
+ int exposure_knee;
int header_read;
u8 header[12]; /* Header without sof marker */
@@ -107,24 +97,16 @@ struct sensor_data {
sensor_init_t *sensor_init;
int sensor_init_size;
int flags;
- unsigned ctrl_dis;
__u8 sensor_addr;
};
/* sensor_data flags */
-#define F_GAIN 0x01 /* has gain */
-#define F_SIF 0x02 /* sif or vga */
-#define F_COARSE_EXPO 0x04 /* exposure control is coarse */
+#define F_SIF 0x01 /* sif or vga */
/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
#define MODE_RAW 0x10 /* raw bayer mode */
#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */
-/* ctrl_dis helper macros */
-#define NO_EXPO ((1 << EXPOSURE) | (1 << AUTOGAIN))
-#define NO_FREQ (1 << FREQ)
-#define NO_BRIGHTNESS (1 << BRIGHTNESS)
-
#define COMP 0xc7 /* 0x87 //0x07 */
#define COMP1 0xc9 /* 0x89 //0x09 */
@@ -133,12 +115,12 @@ struct sensor_data {
#define SYS_CLK 0x04
-#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \
+#define SENS(bridge, sensor, _flags, _sensor_addr) \
{ \
.bridge_init = bridge, \
.sensor_init = sensor, \
.sensor_init_size = sizeof(sensor), \
- .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \
+ .flags = _flags, .sensor_addr = _sensor_addr \
}
/* We calculate the autogain at the end of the transfer of a frame, at this
@@ -147,87 +129,6 @@ struct sensor_data {
the new settings to come into effect before doing any other adjustments. */
#define AUTOGAIN_IGNORE_FRAMES 1
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setbrightness
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define GAIN_KNEE 230
- .default_value = 127,
- },
- .set_control = setgain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 1023,
- .step = 1,
- .default_value = 66,
- /* 33 ms / 30 fps (except on PASXXX) */
-#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */
- .flags = 0,
- },
- .set_control = setexposure
- },
-/* for coarse exposure */
-#define COARSE_EXPOSURE_MIN 2
-#define COARSE_EXPOSURE_MAX 15
-#define COARSE_EXPOSURE_DEF 2 /* 30 fps */
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain (and Exposure)",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- .flags = V4L2_CTRL_FLAG_UPDATE
- },
- .set = sd_setautogain,
- },
-[FREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
-#define FREQ_DEF 0
- .default_value = FREQ_DEF,
- },
- .set_control = setfreq
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -532,25 +433,27 @@ static const __u8 tas5130_sensor_init[][8] = {
};
static const struct sensor_data sensor_data[] = {
-SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
-SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60),
-SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21),
-SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0),
-SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0),
-SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
- NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
- NO_BRIGHTNESS|NO_FREQ, 0),
-SENS(initTas5130, tas5130_sensor_init, F_GAIN,
- NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
+ SENS(initHv7131d, hv7131d_sensor_init, 0, 0),
+ SENS(initHv7131r, hv7131r_sensor_init, 0, 0),
+ SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60),
+ SENS(initOv7630, ov7630_sensor_init, 0, 0x21),
+ SENS(initPas106, pas106_sensor_init, F_SIF, 0),
+ SENS(initPas202, pas202_sensor_init, 0, 0),
+ SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0),
+ SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0),
+ SENS(initTas5130, tas5130_sensor_init, 0, 0),
};
/* get one byte in gspca_dev->usb_buf */
static void reg_r(struct gspca_dev *gspca_dev,
__u16 value)
{
- usb_control_msg(gspca_dev->dev,
+ int res;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ res = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
0, /* request */
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -558,6 +461,12 @@ static void reg_r(struct gspca_dev *gspca_dev,
0, /* index */
gspca_dev->usb_buf, 1,
500);
+
+ if (res < 0) {
+ dev_err(gspca_dev->v4l2_dev.dev,
+ "Error reading register %02x: %d\n", value, res);
+ gspca_dev->usb_err = res;
+ }
}
static void reg_w(struct gspca_dev *gspca_dev,
@@ -565,14 +474,13 @@ static void reg_w(struct gspca_dev *gspca_dev,
const __u8 *buffer,
int len)
{
-#ifdef GSPCA_DEBUG
- if (len > USB_BUF_SZ) {
- PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow");
+ int res;
+
+ if (gspca_dev->usb_err < 0)
return;
- }
-#endif
+
memcpy(gspca_dev->usb_buf, buffer, len);
- usb_control_msg(gspca_dev->dev,
+ res = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x08, /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -580,30 +488,48 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, /* index */
gspca_dev->usb_buf, len,
500);
+
+ if (res < 0) {
+ dev_err(gspca_dev->v4l2_dev.dev,
+ "Error writing register %02x: %d\n", value, res);
+ gspca_dev->usb_err = res;
+ }
}
-static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
+static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
{
int retry = 60;
+ if (gspca_dev->usb_err < 0)
+ return;
+
/* is i2c ready */
reg_w(gspca_dev, 0x08, buffer, 8);
while (retry--) {
+ if (gspca_dev->usb_err < 0)
+ return;
msleep(10);
reg_r(gspca_dev, 0x08);
if (gspca_dev->usb_buf[0] & 0x04) {
- if (gspca_dev->usb_buf[0] & 0x08)
- return -1;
- return 0;
+ if (gspca_dev->usb_buf[0] & 0x08) {
+ dev_err(gspca_dev->v4l2_dev.dev,
+ "i2c write error\n");
+ gspca_dev->usb_err = -EIO;
+ }
+ return;
}
}
- return -1;
+
+ dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n");
+ gspca_dev->usb_err = -EIO;
}
static void i2c_w_vector(struct gspca_dev *gspca_dev,
const __u8 buffer[][8], int len)
{
for (;;) {
+ if (gspca_dev->usb_err < 0)
+ return;
reg_w(gspca_dev, 0x08, *buffer, 8);
len -= 8;
if (len <= 0)
@@ -624,11 +550,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
/* change reg 0x06 */
i2cOV[1] = sensor_data[sd->sensor].sensor_addr;
- i2cOV[3] = sd->ctrls[BRIGHTNESS].val;
- if (i2c_w(gspca_dev, i2cOV) < 0)
- goto err;
+ i2cOV[3] = sd->brightness->val;
+ i2c_w(gspca_dev, i2cOV);
break;
- }
+ }
case SENSOR_PAS106:
case SENSOR_PAS202: {
__u8 i2cpbright[] =
@@ -642,54 +567,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
i2cpdoit[2] = 0x13;
}
- if (sd->ctrls[BRIGHTNESS].val < 127) {
+ if (sd->brightness->val < 127) {
/* change reg 0x0b, signreg */
i2cpbright[3] = 0x01;
/* set reg 0x0c, offset */
- i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val;
+ i2cpbright[4] = 127 - sd->brightness->val;
} else
- i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127;
+ i2cpbright[4] = sd->brightness->val - 127;
- if (i2c_w(gspca_dev, i2cpbright) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpdoit) < 0)
- goto err;
+ i2c_w(gspca_dev, i2cpbright);
+ i2c_w(gspca_dev, i2cpdoit);
+ break;
+ }
+ default:
break;
- }
}
- return;
-err:
- PDEBUG(D_ERR, "i2c error brightness");
}
-static void setsensorgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 gain = sd->ctrls[GAIN].val;
+ u8 gain = gspca_dev->gain->val;
switch (sd->sensor) {
case SENSOR_HV7131D: {
__u8 i2c[] =
{0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17};
- i2c[3] = 0x3f - (gain / 4);
- i2c[4] = 0x3f - (gain / 4);
- i2c[5] = 0x3f - (gain / 4);
+ i2c[3] = 0x3f - gain;
+ i2c[4] = 0x3f - gain;
+ i2c[5] = 0x3f - gain;
- if (i2c_w(gspca_dev, i2c) < 0)
- goto err;
+ i2c_w(gspca_dev, i2c);
break;
- }
+ }
case SENSOR_TAS5110C:
case SENSOR_TAS5130CXX: {
__u8 i2c[] =
{0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
i2c[4] = 255 - gain;
- if (i2c_w(gspca_dev, i2c) < 0)
- goto err;
+ i2c_w(gspca_dev, i2c);
break;
- }
+ }
case SENSOR_TAS5110D: {
__u8 i2c[] = {
0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 };
@@ -703,23 +623,25 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
i2c[3] |= (gain & 0x04) << 3;
i2c[3] |= (gain & 0x02) << 5;
i2c[3] |= (gain & 0x01) << 7;
- if (i2c_w(gspca_dev, i2c) < 0)
- goto err;
+ i2c_w(gspca_dev, i2c);
break;
- }
-
+ }
case SENSOR_OV6650:
- gain >>= 1;
- /* fall thru */
case SENSOR_OV7630: {
__u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ /*
+ * The ov7630's gain is weird, at 32 the gain drops to the
+ * same level as at 16, so skip 32-47 (of the 0-63 scale).
+ */
+ if (sd->sensor == SENSOR_OV7630 && gain >= 32)
+ gain += 16;
+
i2c[1] = sensor_data[sd->sensor].sensor_addr;
- i2c[3] = gain >> 2;
- if (i2c_w(gspca_dev, i2c) < 0)
- goto err;
+ i2c[3] = gain;
+ i2c_w(gspca_dev, i2c);
break;
- }
+ }
case SENSOR_PAS106:
case SENSOR_PAS202: {
__u8 i2cpgain[] =
@@ -737,49 +659,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
i2cpdoit[2] = 0x13;
}
- i2cpgain[3] = gain >> 3;
- i2cpcolorgain[3] = gain >> 4;
- i2cpcolorgain[4] = gain >> 4;
- i2cpcolorgain[5] = gain >> 4;
- i2cpcolorgain[6] = gain >> 4;
-
- if (i2c_w(gspca_dev, i2cpgain) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpdoit) < 0)
- goto err;
- break;
- }
- }
- return;
-err:
- PDEBUG(D_ERR, "i2c error gain");
-}
-
-static void setgain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 gain;
- __u8 buf[3] = { 0, 0, 0 };
+ i2cpgain[3] = gain;
+ i2cpcolorgain[3] = gain >> 1;
+ i2cpcolorgain[4] = gain >> 1;
+ i2cpcolorgain[5] = gain >> 1;
+ i2cpcolorgain[6] = gain >> 1;
- if (sensor_data[sd->sensor].flags & F_GAIN) {
- /* Use the sensor gain to do the actual gain */
- setsensorgain(gspca_dev);
- return;
+ i2c_w(gspca_dev, i2cpgain);
+ i2c_w(gspca_dev, i2cpcolorgain);
+ i2c_w(gspca_dev, i2cpdoit);
+ break;
}
-
- if (sd->bridge == BRIDGE_103) {
- gain = sd->ctrls[GAIN].val >> 1;
- buf[0] = gain; /* Red */
- buf[1] = gain; /* Green */
- buf[2] = gain; /* Blue */
- reg_w(gspca_dev, 0x05, buf, 3);
- } else {
- gain = sd->ctrls[GAIN].val >> 4;
- buf[0] = gain << 4 | gain; /* Red and blue */
- buf[1] = gain; /* Green */
- reg_w(gspca_dev, 0x10, buf, 2);
+ default:
+ if (sd->bridge == BRIDGE_103) {
+ u8 buf[3] = { gain, gain, gain }; /* R, G, B */
+ reg_w(gspca_dev, 0x05, buf, 3);
+ } else {
+ u8 buf[2];
+ buf[0] = gain << 4 | gain; /* Red and blue */
+ buf[1] = gain; /* Green */
+ reg_w(gspca_dev, 0x10, buf, 2);
+ }
}
}
@@ -792,31 +692,24 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* Note the datasheet wrongly says line mode exposure uses reg
0x26 and 0x27, testing has shown 0x25 + 0x26 */
__u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17};
- /* The HV7131D's exposure goes from 0 - 65535, we scale our
- exposure of 0-1023 to 0-6138. There are 2 reasons for this:
- 1) This puts our exposure knee of 200 at approx the point
- where the framerate starts dropping
- 2) At 6138 the framerate has already dropped to 2 fps,
- going any lower makes little sense */
- u16 reg = sd->ctrls[EXPOSURE].val * 6;
+ u16 reg = gspca_dev->exposure->val;
i2c[3] = reg >> 8;
i2c[4] = reg & 0xff;
- if (i2c_w(gspca_dev, i2c) != 0)
- goto err;
+ i2c_w(gspca_dev, i2c);
break;
- }
+ }
case SENSOR_TAS5110C:
case SENSOR_TAS5110D: {
/* register 19's high nibble contains the sn9c10x clock divider
The high nibble configures the no fps according to the
formula: 60 / high_nibble. With a maximum of 30 fps */
- u8 reg = sd->ctrls[EXPOSURE].val;
+ u8 reg = gspca_dev->exposure->val;
reg = (reg << 4) | 0x0b;
reg_w(gspca_dev, 0x19, &reg, 1);
break;
- }
+ }
case SENSOR_OV6650:
case SENSOR_OV7630: {
/* The ov6650 / ov7630 have 2 registers which both influence
@@ -848,7 +741,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
} else
reg10_max = 0x41;
- reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000;
+ reg11 = (15 * gspca_dev->exposure->val + 999) / 1000;
if (reg11 < 1)
reg11 = 1;
else if (reg11 > 16)
@@ -861,16 +754,16 @@ static void setexposure(struct gspca_dev *gspca_dev)
reg11 = 4;
/* frame exposure time in ms = 1000 * reg11 / 30 ->
- reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max
+ reg10 = (gspca_dev->exposure->val / 2) * reg10_max
/ (1000 * reg11 / 30) */
- reg10 = (sd->ctrls[EXPOSURE].val * 15 * reg10_max)
+ reg10 = (gspca_dev->exposure->val * 15 * reg10_max)
/ (1000 * reg11);
/* Don't allow this to get below 10 when using autogain, the
steps become very large (relatively) when below 10 causing
the image to oscilate from much too dark, to much too bright
and back again. */
- if (sd->ctrls[AUTOGAIN].val && reg10 < 10)
+ if (gspca_dev->autogain->val && reg10 < 10)
reg10 = 10;
else if (reg10 > reg10_max)
reg10 = reg10_max;
@@ -884,12 +777,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
if (sd->reg11 == reg11)
i2c[0] = 0xa0;
- if (i2c_w(gspca_dev, i2c) == 0)
+ i2c_w(gspca_dev, i2c);
+ if (gspca_dev->usb_err == 0)
sd->reg11 = reg11;
- else
- goto err;
break;
- }
+ }
case SENSOR_PAS202: {
__u8 i2cpframerate[] =
{0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
@@ -909,28 +801,25 @@ static void setexposure(struct gspca_dev *gspca_dev)
frame exposure times (like we are doing with the ov chips),
as that sometimes leads to jumps in the exposure control,
which are bad for auto exposure. */
- if (sd->ctrls[EXPOSURE].val < 200) {
- i2cpexpo[3] = 255 - (sd->ctrls[EXPOSURE].val * 255)
+ if (gspca_dev->exposure->val < 200) {
+ i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255)
/ 200;
framerate_ctrl = 500;
} else {
/* The PAS202's exposure control goes from 0 - 4095,
but anything below 500 causes vsync issues, so scale
our 200-1023 to 500-4095 */
- framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200)
+ framerate_ctrl = (gspca_dev->exposure->val - 200)
* 1000 / 229 + 500;
}
i2cpframerate[3] = framerate_ctrl >> 6;
i2cpframerate[4] = framerate_ctrl & 0x3f;
- if (i2c_w(gspca_dev, i2cpframerate) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpexpo) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpdoit) < 0)
- goto err;
+ i2c_w(gspca_dev, i2cpframerate);
+ i2c_w(gspca_dev, i2cpexpo);
+ i2c_w(gspca_dev, i2cpdoit);
break;
- }
+ }
case SENSOR_PAS106: {
__u8 i2cpframerate[] =
{0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
@@ -942,46 +831,40 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* For values below 150 use partial frame exposure, above
that use framerate ctrl */
- if (sd->ctrls[EXPOSURE].val < 150) {
- i2cpexpo[3] = 150 - sd->ctrls[EXPOSURE].val;
+ if (gspca_dev->exposure->val < 150) {
+ i2cpexpo[3] = 150 - gspca_dev->exposure->val;
framerate_ctrl = 300;
} else {
/* The PAS106's exposure control goes from 0 - 4095,
but anything below 300 causes vsync issues, so scale
our 150-1023 to 300-4095 */
- framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150)
+ framerate_ctrl = (gspca_dev->exposure->val - 150)
* 1000 / 230 + 300;
}
i2cpframerate[3] = framerate_ctrl >> 4;
i2cpframerate[4] = framerate_ctrl & 0x0f;
- if (i2c_w(gspca_dev, i2cpframerate) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpexpo) < 0)
- goto err;
- if (i2c_w(gspca_dev, i2cpdoit) < 0)
- goto err;
+ i2c_w(gspca_dev, i2cpframerate);
+ i2c_w(gspca_dev, i2cpexpo);
+ i2c_w(gspca_dev, i2cpdoit);
+ break;
+ }
+ default:
break;
- }
}
- return;
-err:
- PDEBUG(D_ERR, "i2c error exposure");
}
static void setfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (sd->sensor) {
- case SENSOR_OV6650:
- case SENSOR_OV7630: {
+ if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) {
/* Framerate adjust register for artificial light 50 hz flicker
compensation, for the ov6650 this is identical to ov6630
0x2b register, see ov6630 datasheet.
0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */
__u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
- switch (sd->ctrls[FREQ].val) {
+ switch (sd->plfreq->val) {
default:
/* case 0: * no filter*/
/* case 2: * 60 hz */
@@ -993,25 +876,17 @@ static void setfreq(struct gspca_dev *gspca_dev)
break;
}
i2c[1] = sensor_data[sd->sensor].sensor_addr;
- if (i2c_w(gspca_dev, i2c) < 0)
- PDEBUG(D_ERR, "i2c error setfreq");
- break;
- }
+ i2c_w(gspca_dev, i2c);
}
}
-#define WANT_REGULAR_AUTOGAIN
-#define WANT_COARSE_EXPO_AUTOGAIN
-#include "autogain_functions.h"
-
static void do_autogain(struct gspca_dev *gspca_dev)
{
- int deadzone, desired_avg_lum, result;
struct sd *sd = (struct sd *) gspca_dev;
- int avg_lum = atomic_read(&sd->avg_lum);
+ int deadzone, desired_avg_lum, avg_lum;
- if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) ||
- avg_lum == -1 || !sd->ctrls[AUTOGAIN].val)
+ avg_lum = atomic_read(&sd->avg_lum);
+ if (avg_lum == -1)
return;
if (sd->autogain_ignore_frames > 0) {
@@ -1030,22 +905,18 @@ static void do_autogain(struct gspca_dev *gspca_dev)
desired_avg_lum = 13000;
}
- if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
- result = coarse_grained_expo_autogain(gspca_dev, avg_lum,
- sd->ctrls[BRIGHTNESS].val
- * desired_avg_lum / 127,
- deadzone);
- else
- result = auto_gain_n_exposure(gspca_dev, avg_lum,
- sd->ctrls[BRIGHTNESS].val
- * desired_avg_lum / 127,
- deadzone, GAIN_KNEE, EXPOSURE_KNEE);
-
- if (result) {
- PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
- (int) sd->ctrls[GAIN].val,
- (int) sd->ctrls[EXPOSURE].val);
- sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+ if (sd->brightness)
+ desired_avg_lum = sd->brightness->val * desired_avg_lum / 127;
+
+ if (gspca_dev->exposure->maximum < 500) {
+ if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+ desired_avg_lum, deadzone))
+ sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+ } else {
+ int gain_knee = gspca_dev->gain->maximum * 9 / 10;
+ if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum,
+ deadzone, gain_knee, sd->exposure_knee))
+ sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
}
}
@@ -1064,14 +935,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = id->driver_info >> 8;
sd->bridge = id->driver_info & 0xff;
- gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis;
-#if AUTOGAIN_DEF
- if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
- gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
-#endif
-
cam = &gspca_dev->cam;
- cam->ctrls = sd->ctrls;
if (!(sensor_data[sd->sensor].flags & F_SIF)) {
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
@@ -1087,18 +951,143 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
const __u8 stop = 0x09; /* Disable stream turn of LED */
- if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
- sd->ctrls[EXPOSURE].min = COARSE_EXPOSURE_MIN;
- sd->ctrls[EXPOSURE].max = COARSE_EXPOSURE_MAX;
- sd->ctrls[EXPOSURE].def = COARSE_EXPOSURE_DEF;
- if (sd->ctrls[EXPOSURE].val > COARSE_EXPOSURE_MAX)
- sd->ctrls[EXPOSURE].val = COARSE_EXPOSURE_DEF;
+ reg_w(gspca_dev, 0x01, &stop, 1);
+
+ return gspca_dev->usb_err;
+}
+
+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 (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ gspca_dev->gain->val = gspca_dev->gain->default_value;
+ gspca_dev->exposure->val = gspca_dev->exposure->default_value;
+ sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
}
- reg_w(gspca_dev, 0x01, &stop, 1);
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+ setexposure(gspca_dev);
+ if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+ setgain(gspca_dev);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setfreq(gspca_dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+
+ if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 ||
+ sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202)
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+ /* Gain range is sensor dependent */
+ switch (sd->sensor) {
+ case SENSOR_OV6650:
+ case SENSOR_PAS106:
+ case SENSOR_PAS202:
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 31, 1, 15);
+ break;
+ case SENSOR_OV7630:
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 47, 1, 31);
+ break;
+ case SENSOR_HV7131D:
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63, 1, 31);
+ break;
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5110D:
+ case SENSOR_TAS5130CXX:
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 127);
+ break;
+ default:
+ if (sd->bridge == BRIDGE_103) {
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 63);
+ } else {
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 15, 1, 7);
+ }
+ }
+
+ /* Exposure range is sensor dependent, and not all have exposure */
+ switch (sd->sensor) {
+ case SENSOR_HV7131D:
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 8191, 1, 482);
+ sd->exposure_knee = 964;
+ break;
+ case SENSOR_OV6650:
+ case SENSOR_OV7630:
+ case SENSOR_PAS106:
+ case SENSOR_PAS202:
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 1023, 1, 66);
+ sd->exposure_knee = 200;
+ break;
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5110D:
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 2, 15, 1, 2);
+ break;
+ }
+
+ if (gspca_dev->exposure) {
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ }
+
+ if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630)
+ sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
return 0;
}
@@ -1242,10 +1231,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
sd->frames_to_drop = 0;
sd->autogain_ignore_frames = 0;
- sd->exp_too_high_cnt = 0;
- sd->exp_too_low_cnt = 0;
+ gspca_dev->exp_too_high_cnt = 0;
+ gspca_dev->exp_too_low_cnt = 0;
atomic_set(&sd->avg_lum, -1);
- return 0;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1387,37 +1376,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ctrls[AUTOGAIN].val = val;
- sd->exp_too_high_cnt = 0;
- sd->exp_too_low_cnt = 0;
-
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
- if (sd->ctrls[AUTOGAIN].val
- && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
- sd->ctrls[EXPOSURE].val = sd->ctrls[EXPOSURE].def;
- sd->ctrls[GAIN].val = sd->ctrls[GAIN].def;
- if (gspca_dev->streaming) {
- sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
- setexposure(gspca_dev);
- setgain(gspca_dev);
- }
- }
-
- if (sd->ctrls[AUTOGAIN].val)
- gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
- else
- gspca_dev->ctrl_inac = 0;
-
- return 0;
-}
-
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1461,10 +1419,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -1529,6 +1486,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index f38faa9b37c3..150b2df40f7f 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -3199,6 +3199,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c
index 070b9c33b517..14d635277d71 100644
--- a/drivers/media/video/gspca/spca1528.c
+++ b/drivers/media/video/gspca/spca1528.c
@@ -33,102 +33,11 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- u8 brightness;
- u8 contrast;
- u8 hue;
- u8 color;
- u8 sharpness;
-
u8 pkt_seq;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 128
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 8,
- .step = 1,
-#define CONTRAST_DEF 1
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define HUE_DEF 0
- .default_value = HUE_DEF,
- },
- .set = sd_sethue,
- .get = sd_gethue,
- },
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 8,
- .step = 1,
-#define COLOR_DEF 1
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolor,
- .get = sd_getcolor,
- },
- {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define SHARPNESS_DEF 0
- .default_value = SHARPNESS_DEF,
- },
- .set = sd_setsharpness,
- .get = sd_getsharpness,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
/* (does not work correctly)
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -259,58 +168,40 @@ static void wait_status_1(struct gspca_dev *gspca_dev)
gspca_dev->usb_err = -ETIME;
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness);
+ reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, val);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast);
+ reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, val);
}
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue);
+ reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, val);
}
-static void setcolor(struct gspca_dev *gspca_dev)
+static void setcolor(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color);
+ reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, val);
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness);
+ reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, val);
}
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->cam.cam_mode = vga_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */
/*fixme: 256 in ms-win traces*/
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->hue = HUE_DEF;
- sd->color = COLOR_DEF;
- sd->sharpness = SHARPNESS_DEF;
-
return 0;
}
@@ -370,14 +261,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* the JPEG quality shall be 85% */
jpeg_set_qual(sd->jpeg_hdr, 85);
- /* set the controls */
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- sethue(gspca_dev);
- setcolor(gspca_dev);
- setsharpness(gspca_dev);
-
- msleep(5);
reg_r(gspca_dev, 0x00, 0x2520, 1);
msleep(8);
@@ -457,103 +340,70 @@ err:
gspca_dev->last_packet_type = DISCARD_PACKET;
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hue = val;
- if (gspca_dev->streaming)
- sethue(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hue;
- return 0;
-}
-
-static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->color = val;
- if (gspca_dev->streaming)
- setcolor(gspca_dev);
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ sethue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolor(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ }
return gspca_dev->usb_err;
}
-static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->color;
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming)
- setsharpness(gspca_dev);
- return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 8, 1, 1);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 0, 255, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 8, 1, 1);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 255, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
@@ -587,6 +437,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index 103984708c77..25cb68d0556d 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -30,18 +30,12 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
MODULE_LICENSE("GPL");
+#define QUALITY 85
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
- u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 85
-
char subtype;
#define AgfaCl20 0
#define AiptekPocketDV 1
@@ -62,59 +56,6 @@ struct sd {
u8 jpeg_hdr[JPEG_HDR_SZ];
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 127
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
-#define CONTRAST_DEF 31
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
-#define COLOR_DEF 31
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -641,10 +582,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
}
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->quality = QUALITY_DEF;
return 0;
}
@@ -673,7 +610,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, QUALITY);
if (sd->subtype == LogitechClickSmart310) {
xmult = 0x16;
@@ -934,122 +871,79 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
reg_w(gspca_dev, 0x00, 0x8167,
- (__u8) (sd->brightness - 128));
+ (__u8) (val - 128));
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
+ reg_w(gspca_dev, 0x00, 0x8168, val);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
+ reg_w(gspca_dev, 0x00, 0x8169, val);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 63, 1, 31);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 63, 1, 31);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
@@ -1089,6 +983,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index 9c16821addd4..3b7f777785b4 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -49,91 +49,6 @@ struct sd {
#define ViewQuestM318B 6
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define MY_BRIGHTNESS 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define MY_CONTRAST 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 64725,
- .step = 1,
- .default_value = 64725,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define MY_COLOR 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- .default_value = 20,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-#define MY_BLUE_BALANCE 3
- {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- },
- .set = sd_setblue_balance,
- .get = sd_getblue_balance,
- },
-#define MY_RED_BALANCE 4
- {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- },
- .set = sd_setred_balance,
- .get = sd_getred_balance,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -1878,42 +1793,32 @@ static int write_vector(struct gspca_dev *gspca_dev,
return 0;
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, val);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
reg_write(gspca_dev->dev, 0x00, 0x00,
- (sd->contrast >> 8) & 0xff);
+ (val >> 8) & 0xff);
reg_write(gspca_dev->dev, 0x00, 0x01,
- sd->contrast & 0xff);
+ val & 0xff);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, val);
}
-static void setblue_balance(struct gspca_dev *gspca_dev)
+static void setblue_balance(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, val);
}
-static void setred_balance(struct gspca_dev *gspca_dev)
+static void setred_balance(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, val);
}
/* this function is called at probe time */
@@ -1927,9 +1832,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
sd->subtype = id->driver_info;
- sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
- sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
- sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
return 0;
}
@@ -2008,13 +1910,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02);
- /* HDG atleast the Intel CreateAndShare needs to have one of its
- * brightness / contrast / color set otherwise it assumes what seems
- * max contrast. Note that strange enough setting any of these is
- * enough to fix the max contrast problem, to be sure we set all 3 */
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setcolors(gspca_dev);
return 0;
}
@@ -2053,103 +1948,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->usb_err = 0;
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->blue_balance = val;
- if (gspca_dev->streaming)
- setblue_balance(gspca_dev);
- return 0;
-}
-
-static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->blue_balance;
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setblue_balance(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setred_balance(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->red_balance = val;
- if (gspca_dev->streaming)
- setred_balance(gspca_dev);
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->red_balance;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 64725, 1, 64725);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 63, 1, 20);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
@@ -2185,6 +2047,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index 1320f35e39f2..bc7d67c3cb04 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -33,34 +33,11 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- u8 brightness;
-
u8 subtype;
#define IntelPCCameraPro 0
#define Nxultra 1
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 127
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -633,7 +610,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(vga_mode);
else /* no 640x480 for IntelPCCameraPro */
cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
- sd->brightness = BRIGHTNESS_DEF;
return 0;
}
@@ -651,11 +627,8 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 brightness = sd->brightness;
-
reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
}
@@ -706,13 +679,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
- ret = reg_write(dev, SPCA50X_REG_USB,
+ return reg_write(dev, SPCA50X_REG_USB,
SPCA50X_USB_CTRL,
SPCA50X_CUSB_ENABLE);
-
- setbrightness(gspca_dev);
-
- return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -756,30 +725,49 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+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 sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- *val = sd->brightness;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
+ .init_controls = sd_init_controls,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
@@ -812,6 +800,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 54eed87672d2..969bb5a4cd93 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -33,83 +33,10 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
- unsigned char hue;
char norme;
char channel;
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x80,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define SD_CONTRAST 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x47,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define SD_COLOR 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x40,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-#define SD_HUE 3
- {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0,
- },
- .set = sd_sethue,
- .get = sd_gethue,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -281,16 +208,11 @@ static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
- sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
- sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
- sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
return 0;
}
@@ -564,121 +486,93 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
spca506_WriteI2c(gspca_dev, 0x01, 0x09);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ sethue(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hue = val;
- if (gspca_dev->streaming)
- sethue(gspca_dev);
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hue;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 0, 255, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -711,6 +605,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index df4e16996461..1286b4170b88 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -32,8 +32,6 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- u8 brightness;
-
u8 subtype;
#define CreativeVista 0
#define HamaUSBSightcam 1
@@ -43,27 +41,6 @@ struct sd {
#define ViewQuestVQ110 5
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 128
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-};
-
static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -1411,7 +1388,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(sif_mode);
sd->subtype = id->driver_info;
- sd->brightness = BRIGHTNESS_DEF;
init_data = init_data_tb[sd->subtype];
return write_vector(gspca_dev, init_data);
@@ -1471,11 +1447,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 brightness = sd->brightness;
-
/* MX seem contrast */
reg_write(gspca_dev->dev, 0x8651, brightness);
reg_write(gspca_dev->dev, 0x8652, brightness);
@@ -1483,31 +1456,50 @@ static void setbrightness(struct gspca_dev *gspca_dev)
reg_write(gspca_dev->dev, 0x8654, brightness);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+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 sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- *val = sd->brightness;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -1541,6 +1533,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index 4a5f209ce719..cfe71dd6747d 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -31,39 +31,17 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
MODULE_LICENSE("GPL");
+#define EXPOSURE_MAX (2047 + 325)
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u16 exposure; /* rev12a only */
-#define EXPOSURE_MIN 1
-#define EXPOSURE_DEF 700 /* == 10 fps */
-#define EXPOSURE_MAX (2047 + 325) /* see setexposure */
-
- __u8 contrast; /* rev72a only */
-#define CONTRAST_MIN 0x00
-#define CONTRAST_DEF 0x20
-#define CONTRAST_MAX 0x3f
-
- __u8 brightness; /* rev72a only */
-#define BRIGHTNESS_MIN 0
-#define BRIGHTNESS_DEF 0x20
-#define BRIGHTNESS_MAX 0x3f
-
- __u8 white;
-#define HUE_MIN 1
-#define HUE_DEF 0x40
-#define HUE_MAX 0x7f
-
- __u8 autogain;
-#define AUTOGAIN_MIN 0
-#define AUTOGAIN_DEF 1
-#define AUTOGAIN_MAX 1
-
- __u8 gain; /* rev12a only */
-#define GAIN_MIN 0
-#define GAIN_DEF 63
-#define GAIN_MAX 255
+ struct { /* hue/contrast control cluster */
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *hue;
+ };
+ struct v4l2_ctrl *autogain;
#define EXPO12A_DEF 3
__u8 expo12a; /* expo/gain? for rev 12a */
@@ -461,12 +439,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = sif_072a_mode;
cam->nmodes = ARRAY_SIZE(sif_072a_mode);
}
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->white = HUE_DEF;
- sd->exposure = EXPOSURE_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->gain = GAIN_DEF;
sd->expo12a = EXPO12A_DEF;
return 0;
}
@@ -491,66 +463,49 @@ static int sd_init_72a(struct gspca_dev *gspca_dev)
return 0;
}
-/* rev 72a only */
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- __u8 value;
+ __u16 reg;
- value = sd->brightness;
+ if (sd->chip_revision == Rev012A)
+ reg = 0x8610;
+ else
+ reg = 0x8611;
- /* offsets for white balance */
- reg_w_val(dev, 0x8611, value); /* R */
- reg_w_val(dev, 0x8612, value); /* Gr */
- reg_w_val(dev, 0x8613, value); /* B */
- reg_w_val(dev, 0x8614, value); /* Gb */
+ reg_w_val(dev, reg + 0, val); /* R */
+ reg_w_val(dev, reg + 1, val); /* Gr */
+ reg_w_val(dev, reg + 2, val); /* B */
+ reg_w_val(dev, reg + 3, val); /* Gb */
}
-static void setwhite(struct gspca_dev *gspca_dev)
+static void setwhite(struct gspca_dev *gspca_dev, s32 white, s32 contrast)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u16 white;
+ struct usb_device *dev = gspca_dev->dev;
__u8 blue, red;
__u16 reg;
/* try to emulate MS-win as possible */
- white = sd->white;
red = 0x20 + white * 3 / 8;
blue = 0x90 - white * 5 / 8;
if (sd->chip_revision == Rev012A) {
reg = 0x8614;
} else {
reg = 0x8651;
- red += sd->contrast - 0x20;
- blue += sd->contrast - 0x20;
+ red += contrast - 0x20;
+ blue += contrast - 0x20;
+ reg_w_val(dev, 0x8652, contrast + 0x20); /* Gr */
+ reg_w_val(dev, 0x8654, contrast + 0x20); /* Gb */
}
- reg_w_val(gspca_dev->dev, reg, red);
- reg_w_val(gspca_dev->dev, reg + 2, blue);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
- __u8 value;
-
- if (sd->chip_revision != Rev072A)
- return;
- value = sd->contrast + 0x20;
-
- /* gains for white balance */
- setwhite(gspca_dev);
-/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
- reg_w_val(dev, 0x8652, value); /* Gr */
-/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
- reg_w_val(dev, 0x8654, value); /* Gb */
+ reg_w_val(dev, reg, red);
+ reg_w_val(dev, reg + 2, blue);
}
/* rev 12a only */
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
int i, expo = 0;
/* Register 0x8309 controls exposure for the spca561,
@@ -572,8 +527,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
int table[] = { 0, 450, 550, 625, EXPOSURE_MAX };
for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
- if (sd->exposure <= table[i + 1]) {
- expo = sd->exposure - table[i];
+ if (val <= table[i + 1]) {
+ expo = val - table[i];
if (i)
expo += 300;
expo |= i << 11;
@@ -587,29 +542,27 @@ static void setexposure(struct gspca_dev *gspca_dev)
}
/* rev 12a only */
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
/* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the
sensitivity when set, so 31 + one of them set == 63, and 15
with both of them set == 63 */
- if (sd->gain < 64)
- gspca_dev->usb_buf[0] = sd->gain;
- else if (sd->gain < 128)
- gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;
+ if (val < 64)
+ gspca_dev->usb_buf[0] = val;
+ else if (val < 128)
+ gspca_dev->usb_buf[0] = (val / 2) | 0x40;
else
- gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;
+ gspca_dev->usb_buf[0] = (val / 4) | 0xc0;
gspca_dev->usb_buf[1] = 0;
reg_w_buf(gspca_dev, 0x8335, 2);
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->autogain)
+ if (val)
sd->ag_cnt = AG_CNT_START;
else
sd->ag_cnt = -1;
@@ -644,9 +597,6 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
memcpy(gspca_dev->usb_buf, Reg8391, 8);
reg_w_buf(gspca_dev, 0x8391, 8);
reg_w_buf(gspca_dev, 0x8390, 8);
- setwhite(gspca_dev);
- setgain(gspca_dev);
- setexposure(gspca_dev);
/* Led ON (bit 3 -> 0 */
reg_w_val(gspca_dev->dev, 0x8114, 0x00);
@@ -654,6 +604,7 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
}
static int sd_start_72a(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
int Clck;
int mode;
@@ -683,9 +634,10 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
reg_w_val(dev, 0x8702, 0x81);
reg_w_val(dev, 0x8500, mode); /* mode */
write_sensor_72a(gspca_dev, rev72a_init_sensor2);
- setcontrast(gspca_dev);
+ setwhite(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue),
+ v4l2_ctrl_g_ctrl(sd->contrast));
/* setbrightness(gspca_dev); * fixme: bad values */
- setautogain(gspca_dev);
+ setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
reg_w_val(dev, 0x8112, 0x10 | 0x20);
return 0;
}
@@ -819,221 +771,96 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-/* rev 72a only */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-/* rev 72a only */
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->usb_err = 0;
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- sd->autogain = val;
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
- return 0;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->white = val;
- if (gspca_dev->streaming)
- setwhite(gspca_dev);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ /* hue/contrast control cluster for 72a */
+ setwhite(gspca_dev, sd->hue->val, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ /* just plain hue control for 12a */
+ setwhite(gspca_dev, ctrl->val, 0);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->white;
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-/* rev12a only */
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls_12a(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, EXPOSURE_MAX, 1, 700);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 63);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
-/* rev12a only */
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls_72a(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x20);
+ sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 1, 0x7f, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 0x3f, 1, 0x20);
+ sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- *val = sd->gain;
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(2, &sd->contrast);
return 0;
}
-/* control tables */
-static const struct ctrl sd_ctrls_12a[] = {
- {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = HUE_MIN,
- .maximum = HUE_MAX,
- .step = 1,
- .default_value = HUE_DEF,
- },
- .set = sd_setwhite,
- .get = sd_getwhite,
- },
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = EXPOSURE_MIN,
- .maximum = EXPOSURE_MAX,
- .step = 1,
- .default_value = EXPOSURE_DEF,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = GAIN_MIN,
- .maximum = GAIN_MAX,
- .step = 1,
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-};
-
-static const struct ctrl sd_ctrls_72a[] = {
- {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = HUE_MIN,
- .maximum = HUE_MAX,
- .step = 1,
- .default_value = HUE_DEF,
- },
- .set = sd_setwhite,
- .get = sd_getwhite,
- },
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = BRIGHTNESS_MIN,
- .maximum = BRIGHTNESS_MAX,
- .step = 1,
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = CONTRAST_MIN,
- .maximum = CONTRAST_MAX,
- .step = 1,
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = AUTOGAIN_MIN,
- .maximum = AUTOGAIN_MAX,
- .step = 1,
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
-};
-
/* sub-driver description */
static const struct sd_desc sd_desc_12a = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls_12a,
- .nctrls = ARRAY_SIZE(sd_ctrls_12a),
+ .init_controls = sd_init_controls_12a,
.config = sd_config,
.init = sd_init_12a,
.start = sd_start_12a,
@@ -1045,8 +872,7 @@ static const struct sd_desc sd_desc_12a = {
};
static const struct sd_desc sd_desc_72a = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls_72a,
- .nctrls = ARRAY_SIZE(sd_ctrls_72a),
+ .init_controls = sd_init_controls_72a,
.config = sd_config,
.init = sd_init_72a,
.start = sd_start_72a,
@@ -1103,6 +929,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 04f54654a026..a8ac97931ad6 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -433,6 +433,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index f34ddb0570c8..2c2f3d2f357f 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -340,6 +340,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
index 1a8ba9b3550a..3e1e486af883 100644
--- a/drivers/media/video/gspca/sq930x.c
+++ b/drivers/media/video/gspca/sq930x.c
@@ -36,8 +36,10 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- u16 expo;
- u8 gain;
+ struct { /* exposure/gain control cluster */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ };
u8 do_ctrl;
u8 gpio[2];
@@ -55,42 +57,6 @@ enum sensors {
SENSOR_OV9630,
};
-static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0x0001,
- .maximum = 0x0fff,
- .step = 1,
-#define EXPO_DEF 0x0356
- .default_value = EXPO_DEF,
- },
- .set = sd_setexpo,
- .get = sd_getexpo,
- },
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0x01,
- .maximum = 0xff,
- .step = 1,
-#define GAIN_DEF 0x8d
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-};
-
static struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -791,7 +757,7 @@ static void lz24bp_ppl(struct sd *sd, u16 ppl)
ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, integclks, intstartclk, frameclks, min_frclk;
@@ -799,7 +765,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
u16 cmd;
u8 buf[15];
- integclks = sd->expo;
+ integclks = expo;
i = 0;
cmd = SQ930_CTRL_SET_EXPOSURE;
@@ -818,7 +784,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
buf[i++] = intstartclk;
buf[i++] = frameclks >> 8;
buf[i++] = frameclks;
- buf[i++] = sd->gain;
+ buf[i++] = gain;
break;
default: /* cmos */
/* case SENSOR_MI0360: */
@@ -834,7 +800,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
buf[i++] = 0x35; /* reg = global gain */
buf[i++] = 0x00; /* val H */
buf[i++] = sensor->i2c_dum;
- buf[i++] = 0x80 + sd->gain / 2; /* val L */
+ buf[i++] = 0x80 + gain / 2; /* val L */
buf[i++] = 0x00;
buf[i++] = 0x00;
buf[i++] = 0x00;
@@ -860,9 +826,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->bulk = 1;
- sd->gain = GAIN_DEF;
- sd->expo = EXPO_DEF;
-
return 0;
}
@@ -1089,7 +1052,8 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
return;
sd->do_ctrl = 0;
- setexposure(gspca_dev);
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
+ v4l2_ctrl_g_ctrl(sd->gain));
gspca_dev->cam.bulk_nurbs = 1;
ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
@@ -1113,48 +1077,55 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
}
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+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;
- sd->gain = val;
- if (gspca_dev->streaming)
- sd->do_ctrl = 1;
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- *val = sd->gain;
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val, sd->gain->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- sd->expo = val;
- if (gspca_dev->streaming)
- sd->do_ctrl = 1;
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
+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;
- *val = sd->expo;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 1, 255, 1, 0x8d);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(2, &sd->exposure);
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
@@ -1194,6 +1165,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 4ae7cc8f463a..8c0982607f25 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -29,86 +29,14 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- COLORS,
- LIGHTFREQ,
- NCTRLS /* number of controls */
-};
+#define QUALITY 50
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct gspca_ctrl ctrls[NCTRLS];
-
- u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 88
-
u8 jpeg_hdr[JPEG_HDR_SZ];
};
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setbrightness
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setcontrast
- },
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- },
- .set_control = setcolors
- },
-[LIGHTFREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 1,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
- .default_value = 1,
- },
- .set_control = setlightfreq
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -255,41 +183,36 @@ static void set_par(struct gspca_dev *gspca_dev,
snd_val(gspca_dev, 0x003f08, parval);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x06000000 /* whiteness */
- + (sd->ctrls[BRIGHTNESS].val << 16);
+ + (val << 16);
set_par(gspca_dev, parval);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x07000000 /* contrast */
- + (sd->ctrls[CONTRAST].val << 16);
+ + (val << 16);
set_par(gspca_dev, parval);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
int parval;
parval = 0x08000000 /* saturation */
- + (sd->ctrls[COLORS].val << 16);
+ + (val << 16);
set_par(gspca_dev, parval);
}
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1
+ set_par(gspca_dev, val == 1
? 0x33640000 /* 50 Hz */
: 0x33780000); /* 60 Hz */
}
@@ -298,12 +221,8 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->cam.cam_mode = vga_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
- gspca_dev->cam.ctrls = sd->ctrls;
- sd->quality = QUALITY_DEF;
return 0;
}
@@ -333,7 +252,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, QUALITY);
/* work on alternate 1 */
usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
@@ -365,14 +284,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x0640, 0);
reg_w(gspca_dev, 0x0650, 0);
reg_w(gspca_dev, 0x0660, 0);
- setbrightness(gspca_dev); /* whiteness */
- setcontrast(gspca_dev); /* contrast */
- setcolors(gspca_dev); /* saturation */
set_par(gspca_dev, 0x09800000); /* Red ? */
set_par(gspca_dev, 0x0a800000); /* Green ? */
set_par(gspca_dev, 0x0b800000); /* Blue ? */
set_par(gspca_dev, 0x0d030000); /* Gamma ? */
- setlightfreq(gspca_dev);
/* start the video flow */
set_par(gspca_dev, 0x01000000);
@@ -435,62 +350,70 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
- break;
- strcpy((char *) menu->name, freq_nm[menu->index]);
- return 0;
- }
- return -EINVAL;
-}
+ gspca_dev->usb_err = 0;
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ }
return gspca_dev->usb_err;
}
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
@@ -516,6 +439,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
index 461ed645f309..67605272aaa8 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/video/gspca/stv0680.c
@@ -46,10 +46,6 @@ struct sd {
u8 current_mode;
};
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-};
-
static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
int size)
{
@@ -318,8 +314,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
@@ -352,6 +346,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index c80f0c0c75b6..9ccfcb1c6479 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -30,18 +30,13 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
MODULE_LICENSE("GPL");
+#define QUALITY 85
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- s8 brightness;
- u8 contrast;
- u8 colors;
- u8 autogain;
- u8 quality;
-#define QUALITY_MIN 70
-#define QUALITY_MAX 95
-#define QUALITY_DEF 85
+ bool autogain;
u8 bridge;
#define BRIDGE_SPCA504 0
@@ -59,75 +54,6 @@ struct sd {
u8 jpeg_hdr[JPEG_HDR_SZ];
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = -128,
- .maximum = 127,
- .step = 1,
-#define BRIGHTNESS_DEF 0
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
-#define CONTRAST_DEF 0x20
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
-#define COLOR_DEF 0x1a
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -597,31 +523,31 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev)
spca504B_PollingDataReady(gspca_dev);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
- reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
+ reg_w_riv(gspca_dev, 0x00, reg, val);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
- reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
+ reg_w_riv(gspca_dev, 0x00, reg, val);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u16 reg;
reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
- reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
+ reg_w_riv(gspca_dev, 0x00, reg, val);
}
static void init_ctl_reg(struct gspca_dev *gspca_dev)
@@ -629,10 +555,6 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int pollreg = 1;
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setcolors(gspca_dev);
-
switch (sd->bridge) {
case BRIDGE_SPCA504:
case BRIDGE_SPCA504C:
@@ -704,11 +626,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(vga_mode2);
break;
}
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->quality = QUALITY_DEF;
return 0;
}
@@ -807,7 +724,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x22); /* JPEG 411 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, QUALITY);
if (sd->bridge == BRIDGE_SPCA504B)
spca504B_setQtable(gspca_dev);
@@ -1012,116 +929,69 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return gspca_dev->usb_err;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ sd->autogain = ctrl->val;
+ break;
+ }
return gspca_dev->usb_err;
}
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- return 0;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 0x20);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 0x1a);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
@@ -1208,6 +1078,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 9b9f85a8e60e..8bc6c3ceec2c 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -34,28 +34,19 @@
#include <linux/slab.h>
#include "gspca.h"
-#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0)
-
MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver");
MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
-
- u8 brightness;
- u8 contrast;
- u8 colors;
- u8 autogain;
- u8 gamma;
- u8 sharpness;
- u8 freq;
- u8 red_gain;
- u8 blue_gain;
- u8 green_gain;
- u8 awb; /* set default r/g/b and activate */
- u8 mirror;
- u8 effect;
+ struct v4l2_ctrl *freq;
+ struct { /* awb / color gains control cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ };
u8 sensor;
u8 button_pressed;
@@ -67,245 +58,31 @@ enum sensors {
SENSOR_LT168G, /* must verify if this is the actual model */
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
-
-static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 14,
- .step = 1,
-#define BRIGHTNESS_DEF 8
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0x0d,
- .step = 1,
-#define CONTRAST_DEF 0x07
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 0,
- .maximum = 0x0f,
- .step = 1,
-#define COLORS_DEF 0x05
- .default_value = COLORS_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-#define GAMMA_MAX 16
-#define GAMMA_DEF 10
- {
- {
- .id = V4L2_CID_GAMMA, /* (gamma on win) */
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = GAMMA_MAX - 1,
- .step = 1,
- .default_value = GAMMA_DEF,
- },
- .set = sd_setgamma,
- .get = sd_getgamma,
- },
- {
- {
- .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight,
- * some apps dont bring up the
- * backligth_compensation control) */
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Low Light",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 0x01
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setlowlight,
- .get = sd_getlowlight,
- },
- {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror Image",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define MIRROR_DEF 0
- .default_value = MIRROR_DEF,
- },
- .set = sd_setmirror,
- .get = sd_getmirror
- },
- {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light Frequency Filter",
- .minimum = 1, /* 1 -> 0x50, 2->0x60 */
- .maximum = 2,
- .step = 1,
-#define FREQ_DEF 1
- .default_value = FREQ_DEF,
- },
- .set = sd_setfreq,
- .get = sd_getfreq},
-
- {
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AWB_DEF 0
- .default_value = AWB_DEF,
- },
- .set = sd_setawb,
- .get = sd_getawb
- },
- {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
-#define SHARPNESS_DEF 0x06
- .default_value = SHARPNESS_DEF,
- },
- .set = sd_setsharpness,
- .get = sd_getsharpness,
- },
- {
- {
- .id = V4L2_CID_EFFECTS,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Webcam Effects",
- .minimum = 0,
- .maximum = 4,
- .step = 1,
-#define EFFECTS_DEF 0
- .default_value = EFFECTS_DEF,
- },
- .set = sd_seteffect,
- .get = sd_geteffect
- },
- {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 0x10,
- .maximum = 0x40,
- .step = 1,
-#define BLUE_GAIN_DEF 0x20
- .default_value = BLUE_GAIN_DEF,
- },
- .set = sd_setblue_gain,
- .get = sd_getblue_gain,
- },
- {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 0x10,
- .maximum = 0x40,
- .step = 1,
-#define RED_GAIN_DEF 0x20
- .default_value = RED_GAIN_DEF,
- },
- .set = sd_setred_gain,
- .get = sd_getred_gain,
- },
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0x10,
- .maximum = 0x40,
- .step = 1,
-#define GAIN_DEF 0x20
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-};
-
static const struct v4l2_pix_format vga_mode_t16[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 4},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 3},
+#endif
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 2},
+#if 0 /* HDG: broken with my test cam, so lets disable it */
{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 352,
.sizeimage = 352 * 288 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 1},
+#endif
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 8 + 590,
@@ -454,17 +231,6 @@ static const struct additional_sensor_data sensor_data[] = {
};
#define MAX_EFFECTS 7
-/* easily done by soft, this table could be removed,
- * i keep it here just in case */
-static char *effects_control[MAX_EFFECTS] = {
- "Normal",
- "Emboss", /* disabled */
- "Monochrome",
- "Sepia",
- "Sketch",
- "Sun Effect", /* disabled */
- "Negative",
-};
static const u8 effects_table[MAX_EFFECTS][6] = {
{0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */
{0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */
@@ -475,7 +241,8 @@ static const u8 effects_table[MAX_EFFECTS][6] = {
{0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */
};
-static const u8 gamma_table[GAMMA_MAX][17] = {
+#define GAMMA_MAX (15)
+static const u8 gamma_table[GAMMA_MAX+1][17] = {
/* gamma table from cam1690.ini */
{0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */
0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb,
@@ -683,38 +450,18 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- cam = &gspca_dev->cam;
+ struct cam *cam = &gspca_dev->cam;
cam->cam_mode = vga_mode_t16;
cam->nmodes = ARRAY_SIZE(vga_mode_t16);
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLORS_DEF;
- sd->gamma = GAMMA_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->mirror = MIRROR_DEF;
- sd->freq = FREQ_DEF;
- sd->awb = AWB_DEF;
- sd->sharpness = SHARPNESS_DEF;
- sd->effect = EFFECTS_DEF;
- sd->red_gain = RED_GAIN_DEF;
- sd->blue_gain = BLUE_GAIN_DEF;
- sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF;
-
return 0;
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 brightness)
{
- struct sd *sd = (struct sd *) gspca_dev;
- unsigned int brightness;
u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
- brightness = sd->brightness;
if (brightness < 7) {
set6[1] = 0x26;
set6[3] = 0x70 - brightness * 0x10;
@@ -725,10 +472,8 @@ static void setbrightness(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, set6, sizeof set6);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 contrast)
{
- struct sd *sd = (struct sd *) gspca_dev;
- unsigned int contrast = sd->contrast;
u16 reg_to_write;
if (contrast < 7)
@@ -739,89 +484,62 @@ static void setcontrast(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, reg_to_write);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u16 reg_to_write;
- reg_to_write = 0x80bb + sd->colors * 0x100; /* was 0xc0 */
+ reg_to_write = 0x80bb + val * 0x100; /* was 0xc0 */
reg_w(gspca_dev, reg_to_write);
}
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
PDEBUG(D_CONF, "Gamma: %d", sd->gamma);
reg_w_ixbuf(gspca_dev, 0x90,
- gamma_table[sd->gamma], sizeof gamma_table[0]);
+ gamma_table[val], sizeof gamma_table[0]);
}
-static void setRGB(struct gspca_dev *gspca_dev)
+static void setawb_n_RGB(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 all_gain_reg[6] =
- {0x87, 0x00, 0x88, 0x00, 0x89, 0x00};
+ u8 all_gain_reg[8] = {
+ 0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00 };
+ s32 red_gain, blue_gain, green_gain;
+
+ green_gain = sd->gain->val;
+
+ red_gain = green_gain + sd->red_balance->val;
+ if (red_gain > 0x40)
+ red_gain = 0x40;
+ else if (red_gain < 0x10)
+ red_gain = 0x10;
+
+ blue_gain = green_gain + sd->blue_balance->val;
+ if (blue_gain > 0x40)
+ blue_gain = 0x40;
+ else if (blue_gain < 0x10)
+ blue_gain = 0x10;
+
+ all_gain_reg[1] = red_gain;
+ all_gain_reg[3] = blue_gain;
+ all_gain_reg[5] = green_gain;
+ all_gain_reg[7] = sensor_data[sd->sensor].reg80;
+ if (!sd->awb->val)
+ all_gain_reg[7] &= ~0x04; /* AWB off */
- all_gain_reg[1] = sd->red_gain;
- all_gain_reg[3] = sd->blue_gain;
- all_gain_reg[5] = sd->green_gain;
reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
}
-/* Generic fnc for r/b balance, exposure and awb */
-static void setawb(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 reg80;
-
- reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80;
-
- /* on awb leave defaults values */
- if (!sd->awb) {
- /* shoud we wait here.. */
- /* update and reset RGB gains with webcam values */
- sd->red_gain = reg_r(gspca_dev, 0x0087);
- sd->blue_gain = reg_r(gspca_dev, 0x0088);
- sd->green_gain = reg_r(gspca_dev, 0x0089);
- reg80 &= ~0x0400; /* AWB off */
- }
- reg_w(gspca_dev, reg80);
- reg_w(gspca_dev, reg80);
-}
-
-static void init_gains(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 reg80;
- u8 all_gain_reg[8] =
- {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00};
-
- all_gain_reg[1] = sd->red_gain;
- all_gain_reg[3] = sd->blue_gain;
- all_gain_reg[5] = sd->green_gain;
- reg80 = sensor_data[sd->sensor].reg80;
- if (!sd->awb)
- reg80 &= ~0x04;
- all_gain_reg[7] = reg80;
- reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
-
- reg_w(gspca_dev, (sd->red_gain << 8) + 0x87);
- reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88);
- reg_w(gspca_dev, (sd->green_gain << 8) + 0x89);
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
u16 reg_to_write;
- reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
+ reg_to_write = 0x0aa6 + 0x1000 * val;
reg_w(gspca_dev, reg_to_write);
}
-static void setfreq(struct gspca_dev *gspca_dev)
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 reg66;
@@ -829,7 +547,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_LT168G:
- if (sd->freq != 0)
+ if (val != 0)
freq[3] = 0xa8;
reg66 = 0x41;
break;
@@ -840,7 +558,7 @@ static void setfreq(struct gspca_dev *gspca_dev)
reg66 = 0x40;
break;
}
- switch (sd->freq) {
+ switch (val) {
case 0: /* no flicker */
freq[3] = 0xf0;
break;
@@ -941,14 +659,9 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e);
-
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setgamma(gspca_dev);
- setcolors(gspca_dev);
- setsharpness(gspca_dev);
- init_gains(gspca_dev);
- setfreq(gspca_dev);
+ reg_w(gspca_dev, (0x20 << 8) + 0x87);
+ reg_w(gspca_dev, (0x20 << 8) + 0x88);
+ reg_w(gspca_dev, (0x20 << 8) + 0x89);
reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5);
reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
@@ -968,31 +681,44 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setmirror(struct gspca_dev *gspca_dev)
+static void setmirror(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 hflipcmd[8] =
{0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
- if (sd->mirror)
+ if (val)
hflipcmd[3] = 0x01;
reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd);
}
-static void seteffect(struct gspca_dev *gspca_dev)
+static void seteffect(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ int idx = 0;
- reg_w_buf(gspca_dev, effects_table[sd->effect],
- sizeof effects_table[0]);
- if (sd->effect == 1 || sd->effect == 5) {
- PDEBUG(D_CONF,
- "This effect have been disabled for webcam \"safety\"");
- return;
+ switch (val) {
+ case V4L2_COLORFX_NONE:
+ break;
+ case V4L2_COLORFX_BW:
+ idx = 2;
+ break;
+ case V4L2_COLORFX_SEPIA:
+ idx = 3;
+ break;
+ case V4L2_COLORFX_SKETCH:
+ idx = 4;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ idx = 6;
+ break;
+ default:
+ break;
}
- if (sd->effect == 1 || sd->effect == 4)
+ reg_w_buf(gspca_dev, effects_table[idx],
+ sizeof effects_table[0]);
+
+ if (val == V4L2_COLORFX_SKETCH)
reg_w(gspca_dev, 0x4aa6);
else
reg_w(gspca_dev, 0xfaa6);
@@ -1070,7 +796,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
sensor = &sensor_data[sd->sensor];
- setfreq(gspca_dev);
+ setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
reg_r(gspca_dev, 0x0012);
reg_w_buf(gspca_dev, t2, sizeof t2);
reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
@@ -1142,296 +868,157 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, pkt_type, data, len);
}
-static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->blue_gain = val;
- if (gspca_dev->streaming)
- reg_w(gspca_dev, (val << 8) + 0x88);
- return 0;
-}
-
-static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->blue_gain;
- return 0;
-}
-
-static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->red_gain = val;
- if (gspca_dev->streaming)
- reg_w(gspca_dev, (val << 8) + 0x87);
-
- return 0;
-}
-
-static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->red_gain;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 psg, nsg;
-
- psg = sd->red_gain + sd->blue_gain + sd->green_gain;
- nsg = val * 3;
- sd->red_gain = sd->red_gain * nsg / psg;
- if (sd->red_gain > 0x40)
- sd->red_gain = 0x40;
- else if (sd->red_gain < 0x10)
- sd->red_gain = 0x10;
- sd->blue_gain = sd->blue_gain * nsg / psg;
- if (sd->blue_gain > 0x40)
- sd->blue_gain = 0x40;
- else if (sd->blue_gain < 0x10)
- sd->blue_gain = 0x10;
- sd->green_gain = sd->green_gain * nsg / psg;
- if (sd->green_gain > 0x40)
- sd->green_gain = 0x40;
- else if (sd->green_gain < 0x10)
- sd->green_gain = 0x10;
-
- if (gspca_dev->streaming)
- setRGB(gspca_dev);
- return 0;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3;
- return 0;
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return *val;
-}
-
-static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->awb = val;
- if (gspca_dev->streaming)
- setawb(gspca_dev);
- return 0;
-}
-
-static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->awb;
- return *val;
-}
-
-static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->mirror = val;
- if (gspca_dev->streaming)
- setmirror(gspca_dev);
- return 0;
-}
-
-static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->mirror;
- return *val;
-}
-
-static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->effect = val;
- if (gspca_dev->streaming)
- seteffect(gspca_dev);
- return 0;
-}
-
-static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->effect;
- return *val;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return *val;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gamma = val;
- if (gspca_dev->streaming)
- setgamma(gspca_dev);
- return 0;
-}
-
-static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gamma;
- return 0;
-}
-
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->freq = val;
- if (gspca_dev->streaming)
- setfreq(gspca_dev);
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+ s32 red_gain, blue_gain, green_gain;
+
+ gspca_dev->usb_err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ red_gain = reg_r(gspca_dev, 0x0087);
+ if (red_gain > 0x40)
+ red_gain = 0x40;
+ else if (red_gain < 0x10)
+ red_gain = 0x10;
+
+ blue_gain = reg_r(gspca_dev, 0x0088);
+ if (blue_gain > 0x40)
+ blue_gain = 0x40;
+ else if (blue_gain < 0x10)
+ blue_gain = 0x10;
+
+ green_gain = reg_r(gspca_dev, 0x0089);
+ if (green_gain > 0x40)
+ green_gain = 0x40;
+ else if (green_gain < 0x10)
+ green_gain = 0x10;
+
+ sd->gain->val = green_gain;
+ sd->red_balance->val = red_gain - green_gain;
+ sd->blue_balance->val = blue_gain - green_gain;
+ break;
+ }
return 0;
}
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- *val = sd->freq;
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- sd->sharpness = val;
- if (gspca_dev->streaming)
- setsharpness(gspca_dev);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAMMA:
+ setgamma(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ setmirror(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ reg_w(gspca_dev, ctrl->val ? 0xf48e : 0xb48e);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ setawb_n_RGB(gspca_dev);
+ break;
+ case V4L2_CID_COLORFX:
+ seteffect(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .g_volatile_ctrl = sd_g_volatile_ctrl,
+ .s_ctrl = sd_s_ctrl,
+};
-/* Low Light set here......*/
-static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 12);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 14, 1, 8);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0x0d, 1, 7);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xf, 1, 5);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 10);
+ /* Activate lowlight, some apps dont bring up the
+ backlight_compensation control) */
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, 0, 1, 1, 1);
+ if (sd->sensor == SENSOR_TAS5130A)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->awb = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0x10, 0x40, 1, 0x20);
+ sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, 0);
+ sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, 0);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 15, 1, 6);
+ v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_COLORFX, V4L2_COLORFX_SKETCH,
+ ~((1 << V4L2_COLORFX_NONE) |
+ (1 << V4L2_COLORFX_BW) |
+ (1 << V4L2_COLORFX_SEPIA) |
+ (1 << V4L2_COLORFX_SKETCH) |
+ (1 << V4L2_COLORFX_NEGATIVE)),
+ V4L2_COLORFX_NONE);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
- sd->autogain = val;
- if (val != 0)
- reg_w(gspca_dev, 0xf48e);
- else
- reg_w(gspca_dev, 0xb48e);
- return 0;
-}
+ v4l2_ctrl_auto_cluster(4, &sd->awb, 0, true);
-static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
return 0;
}
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
-
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
- break;
- strcpy((char *) menu->name, freq_nm[menu->index]);
- return 0;
- case V4L2_CID_EFFECTS:
- if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
- strlcpy((char *) menu->name,
- effects_control[menu->index],
- sizeof menu->name);
- return 0;
- }
- break;
- }
- return -EINVAL;
-}
-
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.other_input = 1,
#endif
@@ -1460,6 +1047,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
index c6326d177a3d..a6055246cb9d 100644
--- a/drivers/media/video/gspca/topro.c
+++ b/drivers/media/video/gspca/topro.c
@@ -120,24 +120,13 @@ static const u8 jpeg_head[] = {
#define JPEG_HDR_SZ 521
};
-enum e_ctrl {
- EXPOSURE,
- QUALITY,
- SHARPNESS,
- RGAIN,
- GAIN,
- BGAIN,
- GAMMA,
- AUTOGAIN,
- NCTRLS /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct gspca_ctrl ctrls[NCTRLS];
+ struct v4l2_ctrl *jpegqual;
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *gamma;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
u8 framerate;
u8 quality; /* webcam current JPEG quality (0..16) */
@@ -1415,32 +1404,33 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev)
}
/* set the gain and exposure */
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
+ s32 blue, s32 red)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->sensor == SENSOR_CX0342) {
- int expo;
-
- expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
+ expo = (expo << 2) - 1;
i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
if (sd->bridge == BRIDGE_TP6800)
i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
- sd->ctrls[GAIN].val >> 8);
- i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
+ gain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
if (sd->bridge == BRIDGE_TP6800)
i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
- sd->ctrls[GAIN].val >> 8);
- i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
- sd->ctrls[BGAIN].val >> 8);
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
- sd->ctrls[RGAIN].val >> 8);
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
+ gain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
+ if (sd->sensor == SENSOR_CX0342) {
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+ blue >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+ red >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
+ }
i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
return;
@@ -1448,10 +1438,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* soi763a */
i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
- sd->ctrls[EXPOSURE].val);
+ expo);
/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
i2c_w(gspca_dev, 0x00, /* gain */
- sd->ctrls[GAIN].val);
+ gain);
}
/* set the JPEG quantization tables */
@@ -1472,12 +1462,10 @@ static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
}
/* set the JPEG compression quality factor */
-static void setquality(struct gspca_dev *gspca_dev)
+static void setquality(struct gspca_dev *gspca_dev, s32 q)
{
struct sd *sd = (struct sd *) gspca_dev;
- u16 q;
- q = sd->ctrls[QUALITY].val;
if (q != 16)
q = 15 - q;
@@ -1508,10 +1496,9 @@ static const u8 color_gain[NSENSORS][18] = {
0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
};
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
{
struct sd *sd = (struct sd *) gspca_dev;
- int gamma;
#define NGAMMA 6
static const u8 gamma_tb[NGAMMA][3][1024] = {
{ /* gamma 0 - from tp6800 + soi763a */
@@ -3836,7 +3823,6 @@ static void setgamma(struct gspca_dev *gspca_dev)
if (sd->bridge == BRIDGE_TP6810)
reg_w(gspca_dev, 0x02, 0x28);
/* msleep(50); */
- gamma = sd->ctrls[GAMMA].val;
bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
@@ -3864,43 +3850,35 @@ static void setgamma(struct gspca_dev *gspca_dev)
/* msleep(50); */
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
if (sd->bridge == BRIDGE_TP6800) {
- val = sd->ctrls[SHARPNESS].val
- | 0x08; /* grid compensation enable */
+ val |= 0x08; /* grid compensation enable */
if (gspca_dev->width == 640)
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
else
val |= 0x04; /* scaling down enable */
reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
} else {
- val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
+ val = (val << 5) | 0x08;
reg_w(gspca_dev, 0x59, val);
}
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
- return;
- if (sd->ctrls[AUTOGAIN].val) {
- sd->ag_cnt = AG_CNT_START;
- gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
- } else {
- sd->ag_cnt = -1;
- gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
- }
+ sd->ag_cnt = val ? AG_CNT_START : -1;
}
/* set the resolution for sensor cx0342 */
static void set_resolution(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
if (gspca_dev->width == 320) {
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
@@ -3926,8 +3904,9 @@ static void set_resolution(struct gspca_dev *gspca_dev)
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
ARRAY_SIZE(color_gain[0]));
- setgamma(gspca_dev);
- setquality(gspca_dev);
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
}
/* convert the frame rate to a tp68x0 value */
@@ -3963,7 +3942,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
return i;
}
-static void setframerate(struct gspca_dev *gspca_dev)
+static void setframerate(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 fr_idx;
@@ -3974,7 +3953,7 @@ static void setframerate(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x7b);
reg_w(gspca_dev, 0x7b,
sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
- if (sd->ctrls[EXPOSURE].val >= 128)
+ if (val >= 128)
fr_idx = 0xf0; /* lower frame rate */
}
@@ -3984,43 +3963,43 @@ static void setframerate(struct gspca_dev *gspca_dev)
i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
}
-static void setrgain(struct gspca_dev *gspca_dev)
+static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int rgain;
-
- rgain = sd->ctrls[RGAIN].val;
i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
}
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ s32 val = gspca_dev->gain->val;
if (sd->sensor == SENSOR_CX0342) {
- sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
- * val / sd->ctrls[GAIN].val;
- if (sd->ctrls[BGAIN].val > 4095)
- sd->ctrls[BGAIN].val = 4095;
- sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
- * val / sd->ctrls[GAIN].val;
- if (sd->ctrls[RGAIN].val > 4095)
- sd->ctrls[RGAIN].val = 4095;
+ s32 old = gspca_dev->gain->cur.val ?
+ gspca_dev->gain->cur.val : 1;
+
+ sd->blue->val = sd->blue->val * val / old;
+ if (sd->blue->val > 4095)
+ sd->blue->val = 4095;
+ sd->red->val = sd->red->val * val / old;
+ if (sd->red->val > 4095)
+ sd->red->val = 4095;
+ }
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, gspca_dev->exposure->val,
+ gspca_dev->gain->val,
+ sd->blue->val, sd->red->val);
+ else
+ setexposure(gspca_dev, gspca_dev->exposure->val,
+ gspca_dev->gain->val, 0, 0);
}
- sd->ctrls[GAIN].val = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
return gspca_dev->usb_err;
}
-static void setbgain(struct gspca_dev *gspca_dev)
+static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int bgain;
-
- bgain = sd->ctrls[BGAIN].val;
i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
@@ -4040,7 +4019,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
framerates : framerates_6810;
sd->framerate = 30; /* default: 30 fps */
- gspca_dev->cam.ctrls = sd->ctrls;
return 0;
}
@@ -4108,32 +4086,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
if (sd->sensor == SENSOR_SOI763A) {
pr_info("Sensor soi763a\n");
- sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
- sd->ctrls[GAIN].max = 15;
- sd->ctrls[GAIN].def = 3;
- gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
if (sd->bridge == BRIDGE_TP6810) {
soi763a_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
- gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
- } else {
- gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
}
} else {
pr_info("Sensor cx0342\n");
if (sd->bridge == BRIDGE_TP6810) {
cx0342_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
- gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
- } else {
- gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
}
}
- if (sd->bridge == BRIDGE_TP6810)
- sd->ctrls[QUALITY].def = 0; /* auto quality */
set_dqt(gspca_dev, 0);
return 0;
}
@@ -4207,8 +4169,9 @@ static void set_led(struct gspca_dev *gspca_dev, int on)
static void cx0342_6800_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
static const struct cmd reg_init[] = {
-/*fixme: is this usefull?*/
+ /* fixme: is this useful? */
{TP6800_R17_GPIO_IO, 0x9f},
{TP6800_R16_GPIO_PD, 0x40},
{TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
@@ -4279,13 +4242,21 @@ static void cx0342_6800_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
- setexposure(gspca_dev);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
set_led(gspca_dev, 1);
set_resolution(gspca_dev);
}
static void cx0342_6810_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
static const struct cmd sensor_init_2[] = {
{CX0342_EXPO_LINE_L, 0x6f},
{CX0342_EXPO_LINE_H, 0x02},
@@ -4366,10 +4337,10 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x07, 0x85);
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
}
- setgamma(gspca_dev);
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
reg_w_buf(gspca_dev, tp6810_bridge_start,
ARRAY_SIZE(tp6810_bridge_start));
- setsharpness(gspca_dev);
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
ARRAY_SIZE(color_gain[0]));
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
@@ -4380,11 +4351,12 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
set_led(gspca_dev, 1);
-/* setquality(gspca_dev); */
+/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
}
static void soi763a_6800_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
static const struct cmd reg_init[] = {
{TP6800_R79_QUALITY, 0x04},
{TP6800_R79_QUALITY, 0x01},
@@ -4484,19 +4456,28 @@ static void soi763a_6800_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
- setsharpness(gspca_dev);
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
ARRAY_SIZE(color_gain[0]));
set_led(gspca_dev, 1);
- setexposure(gspca_dev);
- setquality(gspca_dev);
- setgamma(gspca_dev);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
}
static void soi763a_6810_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
static const struct cmd bridge_init_2[] = {
{TP6800_R7A_BLK_THRLD, 0x00},
{TP6800_R79_QUALITY, 0x04},
@@ -4520,7 +4501,14 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x22, gspca_dev->alt);
bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
reg_w(gspca_dev, 0x59, 0x40);
- setexposure(gspca_dev);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
reg_w_buf(gspca_dev, tp6810_ov_init_common,
ARRAY_SIZE(tp6810_ov_init_common));
@@ -4534,7 +4522,7 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x07, 0x85);
reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
}
- setgamma(gspca_dev);
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
reg_w_buf(gspca_dev, tp6810_bridge_start,
ARRAY_SIZE(tp6810_bridge_start));
@@ -4545,12 +4533,19 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x00, 0x00);
- setsharpness(gspca_dev);
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
ARRAY_SIZE(color_gain[0]));
set_led(gspca_dev, 1);
reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
- setexposure(gspca_dev);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
}
@@ -4576,12 +4571,25 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x80, 0x03);
reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
- setexposure(gspca_dev);
- setquality(gspca_dev);
- setautogain(gspca_dev);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev,
+ v4l2_ctrl_g_ctrl(sd->jpegqual));
+ if (sd->bridge == BRIDGE_TP6810)
+ setautogain(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->autogain));
}
- setframerate(gspca_dev);
+ setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
return gspca_dev->usb_err;
}
@@ -4672,12 +4680,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
-/* -- do autogain -- */
-/* gain setting is done in setexposure() for tp6810 */
-static void setgain(struct gspca_dev *gspca_dev) {}
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
static void sd_dq_callback(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -4739,17 +4741,19 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
luma /= 4;
reg_w(gspca_dev, 0x7d, 0x00);
- expo = sd->ctrls[EXPOSURE].val;
- ret = auto_gain_n_exposure(gspca_dev, luma,
+ expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+ ret = gspca_expo_autogain(gspca_dev, luma,
60, /* desired luma */
6, /* dead zone */
2, /* gain knee */
70); /* expo knee */
sd->ag_cnt = AG_CNT_START;
if (sd->bridge == BRIDGE_TP6810) {
- if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
- || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
- setframerate(gspca_dev);
+ int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+ if ((expo >= 128 && new_expo < 128)
+ || (expo < 128 && new_expo >= 128))
+ setframerate(gspca_dev, new_expo);
}
break;
}
@@ -4789,7 +4793,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
sd->framerate = tpf->denominator / tpf->numerator;
if (gspca_dev->streaming)
- setframerate(gspca_dev);
+ setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
/* Return the actual framerate */
i = get_fr_idx(gspca_dev);
@@ -4806,12 +4810,10 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->sensor == SENSOR_SOI763A)
- jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
-/* else
- fixme: TODO
-*/
- return gspca_dev->usb_err;
+ if (sd->sensor != SENSOR_SOI763A)
+ return -ENOTTY;
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ return 0;
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -4819,118 +4821,109 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
+ if (sd->sensor != SENSOR_SOI763A)
+ return -ENOTTY;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = jpeg_q[sd->quality];
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
}
-static struct ctrl sd_ctrls[NCTRLS] = {
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0x01,
- .maximum = 0xdc,
- .step = 1,
- .default_value = 0x4e,
- },
- .set_control = setexposure
- },
-[QUALITY] = {
- {
- .id = V4L2_CID_PRIVATE_BASE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Compression quality",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 13,
- },
- .set_control = setquality
- },
-[RGAIN] = {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red balance",
- .minimum = 0,
- .maximum = 4095,
- .step = 1,
- .default_value = 256,
- },
- .set_control = setrgain
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 4095,
- .step = 1,
- .default_value = 256,
- },
- .set = sd_setgain
- },
-[BGAIN] = {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue balance",
- .minimum = 0,
- .maximum = 4095,
- .step = 1,
- .default_value = 256,
- },
- .set_control = setbgain
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 2,
- },
- .set_control = setsharpness
- },
-[GAMMA] = {
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = NGAMMA - 1,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setgamma
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = AUTOGAIN_DEF
- },
- .set_control = setautogain
- },
+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) {
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAMMA:
+ setgamma(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setbgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setrgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ sd_setgain(gspca_dev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val)
+ break;
+ sd_setgain(gspca_dev);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ jpeg_set_qual(sd->jpeg_hdr, ctrl->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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
+ if (sd->sensor == SENSOR_CX0342) {
+ sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
+ sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
+ }
+ if (sd->sensor == SENSOR_SOI763A)
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 15, 1, 3);
+ else
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 4095, 1, 256);
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 3, 1, 2);
+ sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
+ (sd->sensor == SENSOR_SOI763A &&
+ sd->bridge == BRIDGE_TP6800) ? 0 : 1);
+ if (sd->bridge == BRIDGE_TP6810)
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (sd->sensor == SENSOR_SOI763A)
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ else
+ v4l2_ctrl_cluster(2, &gspca_dev->exposure);
+ return 0;
+}
+
static const struct sd_desc sd_desc = {
.name = KBUILD_MODNAME,
- .ctrls = sd_ctrls,
- .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index c8922c5ffbf5..8591324a53e1 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -30,49 +30,9 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u16 exposure;
- __u16 gain;
-
__u8 packet;
};
-/* V4L2 controls supported by the driver */
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 1,
- .maximum = 0x18f,
- .step = 1,
-#define EXPOSURE_DEF 0x18f
- .default_value = EXPOSURE_DEF,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 0x7ff,
- .step = 1,
-#define GAIN_DEF 0x100
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-};
-
static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
@@ -202,15 +162,12 @@ static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
cam = &gspca_dev->cam;
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
- sd->exposure = EXPOSURE_DEF;
- sd->gain = GAIN_DEF;
return 0;
}
@@ -241,23 +198,19 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure);
+ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, val);
reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
/* 0x84 */
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain);
- reg_w2(gspca_dev, R22_GAIN_RL, sd->gain);
- reg_w2(gspca_dev, R24_GAIN_BL, sd->gain);
- reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain);
+ reg_w2(gspca_dev, R20_GAIN_G1L, val);
+ reg_w2(gspca_dev, R22_GAIN_RL, val);
+ reg_w2(gspca_dev, R24_GAIN_BL, val);
+ reg_w2(gspca_dev, R26_GAIN_G2L, val);
}
/* -- start the camera -- */
@@ -289,9 +242,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
tv_8532_setReg(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
-
/************************************************/
reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
msleep(200);
@@ -339,49 +289,55 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data + gspca_dev->width + 5, gspca_dev->width);
}
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (!gspca_dev->streaming)
+ return 0;
- *val = sd->exposure;
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0x18f, 1, 0x18f);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0x7ff, 1, 0x100);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -415,6 +371,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 208f6b2d512a..f21fd1677c38 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -33,18 +33,10 @@ MODULE_LICENSE("GPL");
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
-
- u8 brightness;
- u8 contrast;
- u8 colors;
- u8 hflip;
- u8 vflip;
- u8 lightfreq;
- s8 sharpness;
- u16 exposure;
- u8 gain;
- u8 autogain;
- u8 backlight;
+ struct { /* hvflip cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
u8 image_offset;
@@ -73,252 +65,6 @@ enum sensors {
NSENSORS
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define BRIGHTNESS_IDX 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define BRIGHTNESS_DEF 128
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define CONTRAST_IDX 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
-#define CONTRAST_DEF 127
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define COLORS_IDX 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 1,
- .maximum = 127,
- .step = 1,
-#define COLOR_DEF 63
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
-/* next 2 controls work with some sensors only */
-#define HFLIP_IDX 3
- {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define HFLIP_DEF 0
- .default_value = HFLIP_DEF,
- },
- .set = sd_sethflip,
- .get = sd_gethflip,
- },
-#define VFLIP_IDX 4
- {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vflip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define VFLIP_DEF 0
- .default_value = VFLIP_DEF,
- },
- .set = sd_setvflip,
- .get = sd_getvflip,
- },
-#define LIGHTFREQ_IDX 5
- {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: No, 1: 50Hz, 2:60Hz */
- .step = 1,
-#define FREQ_DEF 1
- .default_value = FREQ_DEF,
- },
- .set = sd_setfreq,
- .get = sd_getfreq,
- },
-#define SHARPNESS_IDX 6
- {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = -1,
- .maximum = 2,
- .step = 1,
-#define SHARPNESS_DEF -1
- .default_value = SHARPNESS_DEF,
- },
- .set = sd_setsharpness,
- .get = sd_getsharpness,
- },
-#define GAIN_IDX 7
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 78,
- .step = 1,
-#define GAIN_DEF 0
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-#define EXPOSURE_IDX 8
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
-#define EXPOSURE_DEF 450
- .minimum = 0,
- .maximum = 4095,
- .step = 1,
- .default_value = EXPOSURE_DEF,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
-#define AUTOGAIN_IDX 9
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain and Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
-#define BACKLIGHT_IDX 10
- {
- {
- .id = V4L2_CID_BACKLIGHT_COMPENSATION,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Backlight Compensation",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
-#define BACKLIGHT_DEF 15
- .default_value = BACKLIGHT_DEF,
- },
- .set = sd_setbacklight,
- .get = sd_getbacklight,
- },
-};
-
-/* table of the disabled controls */
-static u32 ctrl_dis[NSENSORS] = {
- [SENSOR_HV7131R] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_MI0360] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_MI1310_SOC] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_MI1320] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_MI1320_SOC] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_OV7660] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_OV7670] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_PO1200] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_PO3130NC] =
- (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX)
- | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
- [SENSOR_POxxxx] =
- (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
-};
static const struct v4l2_pix_format vc0321_mode[] = {
{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
@@ -3405,18 +3151,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
(id->idProduct == 0x0892 || id->idProduct == 0x0896))
sd->sensor = SENSOR_POxxxx; /* no probe */
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->hflip = HFLIP_DEF;
- sd->vflip = VFLIP_DEF;
- sd->lightfreq = FREQ_DEF;
- sd->sharpness = SHARPNESS_DEF;
- sd->gain = GAIN_DEF;
- sd->exposure = EXPOSURE_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->backlight = BACKLIGHT_DEF;
-
return 0;
}
@@ -3512,7 +3246,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
}
cam->npkt = npkt[sd->sensor];
- gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
if (sd->sensor == SENSOR_OV7670)
sd->flags |= FL_HFLIP | FL_VFLIP;
@@ -3534,14 +3267,11 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 data;
- if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
- return;
- data = sd->brightness;
+ data = val;
if (data >= 0x80)
data &= 0x7f;
else
@@ -3549,36 +3279,27 @@ static void setbrightness(struct gspca_dev *gspca_dev)
i2c_write(gspca_dev, 0x98, &data, 1);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, u8 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
- return;
- i2c_write(gspca_dev, 0x99, &sd->contrast, 1);
+ i2c_write(gspca_dev, 0x99, &val, 1);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, u8 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 data;
- if (gspca_dev->ctrl_dis & (1 << COLORS_IDX))
- return;
- data = sd->colors - (sd->colors >> 3) - 1;
+ data = val - (val >> 3) - 1;
i2c_write(gspca_dev, 0x94, &data, 1);
- i2c_write(gspca_dev, 0x95, &sd->colors, 1);
+ i2c_write(gspca_dev, 0x95, &val, 1);
}
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 data[2], hflip, vflip;
+ u8 data[2];
- hflip = sd->hflip;
if (sd->flags & FL_HFLIP)
hflip = !hflip;
- vflip = sd->vflip;
if (sd->flags & FL_VFLIP)
vflip = !vflip;
switch (sd->sensor) {
@@ -3610,7 +3331,7 @@ static void sethvflip(struct gspca_dev *gspca_dev)
}
}
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
static const u8 (*ov7660_freq_tb[3])[4] =
@@ -3618,10 +3339,10 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
if (sd->sensor != SENSOR_OV7660)
return;
- usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
+ usb_exchange(gspca_dev, ov7660_freq_tb[val]);
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data;
@@ -3630,51 +3351,41 @@ static void setsharpness(struct gspca_dev *gspca_dev)
case SENSOR_PO1200:
data = 0;
i2c_write(gspca_dev, 0x03, &data, 1);
- if (sd->sharpness < 0)
+ if (val < 0)
data = 0x6a;
else
- data = 0xb5 + sd->sharpness * 3;
+ data = 0xb5 + val * 3;
i2c_write(gspca_dev, 0x61, &data, 1);
break;
case SENSOR_POxxxx:
- if (sd->sharpness < 0)
+ if (val < 0)
data = 0x7e; /* def = max */
else
- data = 0x60 + sd->sharpness * 0x0f;
+ data = 0x60 + val * 0x0f;
i2c_write(gspca_dev, 0x59, &data, 1);
break;
}
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
- return;
- i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+ i2c_write(gspca_dev, 0x15, &val, 1);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 data;
- if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
- return;
- data = sd->exposure >> 8;
+ data = val >> 8;
i2c_write(gspca_dev, 0x1a, &data, 1);
- data = sd->exposure;
+ data = val;
i2c_write(gspca_dev, 0x1b, &data, 1);
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
static const u8 data[2] = {0x28, 0x3c};
- if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
- return;
- i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+ i2c_write(gspca_dev, 0xd1, &data[val], 1);
}
static void setgamma(struct gspca_dev *gspca_dev)
@@ -3683,30 +3394,29 @@ static void setgamma(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, poxxxx_gamma);
}
-static void setbacklight(struct gspca_dev *gspca_dev)
+static void setbacklight(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u16 v;
u8 data;
- data = (sd->backlight << 4) | 0x0f;
+ data = (val << 4) | 0x0f;
i2c_write(gspca_dev, 0xaa, &data, 1);
- v = 613 + 12 * sd->backlight;
+ v = 613 + 12 * val;
data = v >> 8;
i2c_write(gspca_dev, 0xc4, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc5, &data, 1);
- v = 1093 - 12 * sd->backlight;
+ v = 1093 - 12 * val;
data = v >> 8;
i2c_write(gspca_dev, 0xc6, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc7, &data, 1);
- v = 342 + 9 * sd->backlight;
+ v = 342 + 9 * val;
data = v >> 8;
i2c_write(gspca_dev, 0xc8, &data, 1);
data = v;
i2c_write(gspca_dev, 0xc9, &data, 1);
- v = 702 - 9 * sd->backlight;
+ v = 702 - 9 * val;
data = v >> 8;
i2c_write(gspca_dev, 0xca, &data, 1);
data = v;
@@ -3833,14 +3543,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* case SENSOR_POxxxx: */
usb_exchange(gspca_dev, poxxxx_init_common);
setgamma(gspca_dev);
- setbacklight(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setcolors(gspca_dev);
- setsharpness(gspca_dev);
- setautogain(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
usb_exchange(gspca_dev, poxxxx_init_start_3);
if (mode)
init = poxxxx_initQVGA;
@@ -3873,8 +3575,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
msleep(100);
- sethvflip(gspca_dev);
- setlightfreq(gspca_dev);
}
switch (sd->sensor) {
case SENSOR_OV7670:
@@ -3960,233 +3660,152 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hflip;
- return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->vflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- *val = sd->vflip;
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->lightfreq = val;
- if (gspca_dev->streaming)
- setlightfreq(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->lightfreq;
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming)
- setsharpness(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
+ if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+ return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ setbacklight(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ }
return gspca_dev->usb_err;
}
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->backlight = val;
- if (gspca_dev->streaming)
- setbacklight(gspca_dev);
-
- return gspca_dev->usb_err;
-}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ bool has_brightness = false;
+ bool has_contrast = false;
+ bool has_sat = false;
+ bool has_hvflip = false;
+ bool has_freq = false;
+ bool has_backlight = false;
+ bool has_exposure = false;
+ bool has_autogain = false;
+ bool has_gain = false;
+ bool has_sharpness = false;
- *val = sd->backlight;
- return 0;
-}
-
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360:
+ case SENSOR_PO3130NC:
+ break;
+ case SENSOR_MI1310_SOC:
+ case SENSOR_MI1320:
+ case SENSOR_MI1320_SOC:
+ case SENSOR_OV7660:
+ has_hvflip = true;
+ break;
+ case SENSOR_OV7670:
+ has_hvflip = has_freq = true;
+ break;
+ case SENSOR_PO1200:
+ has_hvflip = has_sharpness = true;
+ break;
+ case SENSOR_POxxxx:
+ has_brightness = has_contrast = has_sat = has_backlight =
+ has_exposure = has_autogain = has_gain =
+ has_sharpness = true;
+ break;
+ }
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- if (menu->index >= ARRAY_SIZE(freq_nm))
- break;
- strcpy((char *) menu->name, freq_nm[menu->index]);
- return 0;
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 8);
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ if (has_sat)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 1, 127, 1, 63);
+ if (has_hvflip) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
}
- return -EINVAL;
+ if (has_sharpness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, -1, 2, 1, -1);
+ if (has_freq)
+ v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+ if (has_autogain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (has_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 78, 1, 0);
+ if (has_exposure)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 4095, 1, 450);
+ if (has_backlight)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (sd->hflip)
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
+ .init_controls = sd_init_controls,
.config = sd_config,
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
};
/* -- module initialisation -- */
@@ -4227,6 +3846,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
index 15a30f7a4b2a..b1a64b912666 100644
--- a/drivers/media/video/gspca/vicam.c
+++ b/drivers/media/video/gspca/vicam.c
@@ -44,17 +44,10 @@ MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(VICAM_FIRMWARE);
-enum e_ctrl {
- GAIN,
- EXPOSURE,
- NCTRL /* number of controls */
-};
-
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct work_struct work_struct;
struct workqueue_struct *work_thread;
- struct gspca_ctrl ctrls[NCTRL];
};
/* The vicam sensor has a resolution of 512 x 244, with I believe square
@@ -86,31 +79,6 @@ static struct v4l2_pix_format vicam_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,},
};
-static const struct ctrl sd_ctrls[] = {
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 200,
- },
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 2047,
- .step = 1,
- .default_value = 256,
- },
- },
-};
-
static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
u16 value, u16 index, u8 *data, u16 len)
{
@@ -146,12 +114,13 @@ static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state)
*/
static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
{
- struct sd *sd = (struct sd *)gspca_dev;
int ret, unscaled_height, act_len = 0;
u8 *req_data = gspca_dev->usb_buf;
+ s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+ s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
memset(req_data, 0, 16);
- req_data[0] = sd->ctrls[GAIN].val;
+ req_data[0] = gain;
if (gspca_dev->width == 256)
req_data[1] |= 0x01; /* low nibble x-scale */
if (gspca_dev->height <= 122) {
@@ -167,9 +136,9 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
else /* Up to 244 lines with req_data[3] == 0x08 */
req_data[3] = 0x08; /* vend? */
- if (sd->ctrls[EXPOSURE].val < 256) {
+ if (expo < 256) {
/* Frame rate maxed out, use partial frame expo time */
- req_data[4] = 255 - sd->ctrls[EXPOSURE].val;
+ req_data[4] = 255 - expo;
req_data[5] = 0x00;
req_data[6] = 0x00;
req_data[7] = 0x01;
@@ -177,8 +146,8 @@ static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
/* Modify frame rate */
req_data[4] = 0x00;
req_data[5] = 0x00;
- req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF;
- req_data[7] = sd->ctrls[EXPOSURE].val >> 8;
+ req_data[6] = expo & 0xFF;
+ req_data[7] = expo >> 8;
}
req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */
/* bytes 9-15 do not seem to affect exposure or image quality */
@@ -260,7 +229,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->bulk_size = 64;
cam->cam_mode = vicam_mode;
cam->nmodes = ARRAY_SIZE(vicam_mode);
- cam->ctrls = sd->ctrls;
INIT_WORK(&sd->work_struct, vicam_dostream);
@@ -335,6 +303,24 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
vicam_set_camera_power(gspca_dev, 0);
}
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_EXPOSURE, 0, 2047, 1, 256);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_GAIN, 0, 255, 1, 200);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
/* Table of supported USB devices */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x04c1, 0x009d)},
@@ -347,10 +333,9 @@ MODULE_DEVICE_TABLE(usb, device_table);
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stop0 = sd_stop0,
};
@@ -373,6 +358,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
index 27d2cef0692a..9e3a909e0a00 100644
--- a/drivers/media/video/gspca/w996Xcf.c
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -404,9 +404,14 @@ static void w9968cf_set_crop_window(struct sd *sd)
}
if (sd->sensor == SEN_OV7620) {
- /* Sigh, this is dependend on the clock / framerate changes
- made by the frequency control, sick. */
- if (sd->ctrls[FREQ].val == 1) {
+ /*
+ * Sigh, this is dependend on the clock / framerate changes
+ * made by the frequency control, sick.
+ *
+ * Note we cannot use v4l2_ctrl_g_ctrl here, as we get called
+ * from ov519.c:setfreq() with the ctrl lock held!
+ */
+ if (sd->freq->val == 1) {
start_cropx = 277;
start_cropy = 37;
} else {
@@ -474,8 +479,9 @@ static void w9968cf_mode_init_regs(struct sd *sd)
/* We may get called multiple times (usb isoc bw negotiat.) */
jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
sd->gspca_dev.width, 0x22); /* JPEG 420 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
w9968cf_upload_quantizationtables(sd);
+ v4l2_ctrl_grab(sd->jpegqual, true);
}
/* Video Capture Control Register */
@@ -514,6 +520,7 @@ static void w9968cf_mode_init_regs(struct sd *sd)
static void w9968cf_stop0(struct sd *sd)
{
+ v4l2_ctrl_grab(sd->jpegqual, false);
reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
reg_w(sd, 0x16, 0x0000); /* stop video capture */
}
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
index ecada178bceb..13b8d395d210 100644
--- a/drivers/media/video/gspca/xirlink_cit.c
+++ b/drivers/media/video/gspca/xirlink_cit.c
@@ -53,6 +53,7 @@ MODULE_PARM_DESC(rca_input,
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct v4l2_ctrl *lighting;
u8 model;
#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
@@ -65,127 +66,10 @@ struct sd {
u8 stop_on_control_change;
u8 sof_read;
u8 sof_len;
- u8 contrast;
- u8 brightness;
- u8 hue;
- u8 sharpness;
- u8 lighting;
- u8 hflip;
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
static void sd_stop0(struct gspca_dev *gspca_dev);
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
-#define BRIGHTNESS_DEFAULT 32
- .default_value = BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define SD_CONTRAST 1
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "contrast",
- .minimum = 0,
- .maximum = 20,
- .step = 1,
-#define CONTRAST_DEFAULT 10
- .default_value = CONTRAST_DEFAULT,
- .flags = 0,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-#define SD_HUE 2
- {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
-#define HUE_DEFAULT 63
- .default_value = HUE_DEFAULT,
- .flags = 0,
- },
- .set = sd_sethue,
- .get = sd_gethue,
- },
-#define SD_SHARPNESS 3
- {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 6,
- .step = 1,
-#define SHARPNESS_DEFAULT 3
- .default_value = SHARPNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setsharpness,
- .get = sd_getsharpness,
- },
-#define SD_LIGHTING 4
- {
- {
- .id = V4L2_CID_BACKLIGHT_COMPENSATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Lighting",
- .minimum = 0,
- .maximum = 2,
- .step = 1,
-#define LIGHTING_DEFAULT 1
- .default_value = LIGHTING_DEFAULT,
- .flags = 0,
- },
- .set = sd_setlighting,
- .get = sd_getlighting,
- },
-#define SD_HFLIP 5
- {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define HFLIP_DEFAULT 0
- .default_value = HFLIP_DEFAULT,
- },
- .set = sd_sethflip,
- .get = sd_gethflip,
- },
-};
-
static const struct v4l2_pix_format cif_yuv_mode[] = {
{176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
.bytesperline = 176,
@@ -995,56 +879,36 @@ static int sd_config(struct gspca_dev *gspca_dev,
case CIT_MODEL0:
cam->cam_mode = model0_mode;
cam->nmodes = ARRAY_SIZE(model0_mode);
- gspca_dev->ctrl_dis = ~((1 << SD_CONTRAST) | (1 << SD_HFLIP));
sd->sof_len = 4;
break;
case CIT_MODEL1:
cam->cam_mode = cif_yuv_mode;
cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
- gspca_dev->ctrl_dis = (1 << SD_HUE) | (1 << SD_HFLIP);
sd->sof_len = 4;
break;
case CIT_MODEL2:
cam->cam_mode = model2_mode + 1; /* no 160x120 */
cam->nmodes = 3;
- gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
- (1 << SD_SHARPNESS) |
- (1 << SD_HFLIP);
break;
case CIT_MODEL3:
cam->cam_mode = vga_yuv_mode;
cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
- gspca_dev->ctrl_dis = (1 << SD_HUE) |
- (1 << SD_LIGHTING) |
- (1 << SD_HFLIP);
sd->stop_on_control_change = 1;
sd->sof_len = 4;
break;
case CIT_MODEL4:
cam->cam_mode = model2_mode;
cam->nmodes = ARRAY_SIZE(model2_mode);
- gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
- (1 << SD_SHARPNESS) |
- (1 << SD_LIGHTING) |
- (1 << SD_HFLIP);
break;
case CIT_IBM_NETCAM_PRO:
cam->cam_mode = vga_yuv_mode;
cam->nmodes = 2; /* no 640 x 480 */
cam->input_flags = V4L2_IN_ST_VFLIP;
- gspca_dev->ctrl_dis = ~(1 << SD_CONTRAST);
sd->stop_on_control_change = 1;
sd->sof_len = 4;
break;
}
- sd->brightness = BRIGHTNESS_DEFAULT;
- sd->contrast = CONTRAST_DEFAULT;
- sd->hue = HUE_DEFAULT;
- sd->sharpness = SHARPNESS_DEFAULT;
- sd->lighting = LIGHTING_DEFAULT;
- sd->hflip = HFLIP_DEFAULT;
-
return 0;
}
@@ -1287,7 +1151,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static int cit_set_brightness(struct gspca_dev *gspca_dev)
+static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
@@ -1299,19 +1163,19 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
break;
case CIT_MODEL1:
/* Model 1: Brightness range 0 - 63 */
- cit_Packet_Format1(gspca_dev, 0x0031, sd->brightness);
- cit_Packet_Format1(gspca_dev, 0x0032, sd->brightness);
- cit_Packet_Format1(gspca_dev, 0x0033, sd->brightness);
+ cit_Packet_Format1(gspca_dev, 0x0031, val);
+ cit_Packet_Format1(gspca_dev, 0x0032, val);
+ cit_Packet_Format1(gspca_dev, 0x0033, val);
break;
case CIT_MODEL2:
/* Model 2: Brightness range 0x60 - 0xee */
/* Scale 0 - 63 to 0x60 - 0xee */
- i = 0x60 + sd->brightness * 2254 / 1000;
+ i = 0x60 + val * 2254 / 1000;
cit_model2_Packet1(gspca_dev, 0x001a, i);
break;
case CIT_MODEL3:
/* Model 3: Brightness range 'i' in [0x0C..0x3F] */
- i = sd->brightness;
+ i = val;
if (i < 0x0c)
i = 0x0c;
cit_model3_Packet1(gspca_dev, 0x0036, i);
@@ -1319,7 +1183,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
case CIT_MODEL4:
/* Model 4: Brightness range 'i' in [0x04..0xb4] */
/* Scale 0 - 63 to 0x04 - 0xb4 */
- i = 0x04 + sd->brightness * 2794 / 1000;
+ i = 0x04 + val * 2794 / 1000;
cit_model4_BrightnessPacket(gspca_dev, i);
break;
}
@@ -1327,7 +1191,7 @@ static int cit_set_brightness(struct gspca_dev *gspca_dev)
return 0;
}
-static int cit_set_contrast(struct gspca_dev *gspca_dev)
+static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1335,16 +1199,16 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
case CIT_MODEL0: {
int i;
/* gain 0-15, 0-20 -> 0-15 */
- i = sd->contrast * 1000 / 1333;
+ i = val * 1000 / 1333;
cit_write_reg(gspca_dev, i, 0x0422);
/* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
- i = sd->contrast * 2000 / 1333;
+ i = val * 2000 / 1333;
cit_write_reg(gspca_dev, i, 0x0423);
/* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */
- i = sd->contrast * 4000 / 1333;
+ i = val * 4000 / 1333;
cit_write_reg(gspca_dev, i, 0x0424);
/* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
- i = sd->contrast * 8000 / 1333;
+ i = val * 8000 / 1333;
cit_write_reg(gspca_dev, i, 0x0425);
break;
}
@@ -1355,7 +1219,7 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
case CIT_MODEL1:
{
/* Scale 0 - 20 to 15 - 0 */
- int i, new_contrast = (20 - sd->contrast) * 1000 / 1333;
+ int i, new_contrast = (20 - val) * 1000 / 1333;
for (i = 0; i < cit_model1_ntries; i++) {
cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
cit_send_FF_04_02(gspca_dev);
@@ -1377,20 +1241,20 @@ static int cit_set_contrast(struct gspca_dev *gspca_dev)
{ 0x01, 0x0e, 0x16 },
{ 0x01, 0x10, 0x16 } /* Maximum */
};
- int i = sd->contrast / 3;
+ int i = val / 3;
cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
break;
}
case CIT_IBM_NETCAM_PRO:
- cit_model3_Packet1(gspca_dev, 0x005b, sd->contrast + 1);
+ cit_model3_Packet1(gspca_dev, 0x005b, val + 1);
break;
}
return 0;
}
-static int cit_set_hue(struct gspca_dev *gspca_dev)
+static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1401,7 +1265,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
/* No hue control for these models */
break;
case CIT_MODEL2:
- cit_model2_Packet1(gspca_dev, 0x0024, sd->hue);
+ cit_model2_Packet1(gspca_dev, 0x0024, val);
/* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
break;
case CIT_MODEL3: {
@@ -1409,7 +1273,7 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
/* TESTME according to the ibmcam driver this does not work */
if (0) {
/* Scale 0 - 127 to 0x05 - 0x37 */
- int i = 0x05 + sd->hue * 1000 / 2540;
+ int i = 0x05 + val * 1000 / 2540;
cit_model3_Packet1(gspca_dev, 0x007e, i);
}
break;
@@ -1435,14 +1299,14 @@ static int cit_set_hue(struct gspca_dev *gspca_dev)
cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */
cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */
cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, sd->hue, 0x012d); /* Hue */
+ cit_write_reg(gspca_dev, val, 0x012d); /* Hue */
cit_write_reg(gspca_dev, 0xf545, 0x0124);
break;
}
return 0;
}
-static int cit_set_sharpness(struct gspca_dev *gspca_dev)
+static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1459,7 +1323,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
for (i = 0; i < cit_model1_ntries; i++)
- cit_PacketFormat2(gspca_dev, 0x0013, sa[sd->sharpness]);
+ cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
break;
}
case CIT_MODEL3:
@@ -1482,10 +1346,10 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
{ 0x03, 0x06, 0x05, 0x14 },
{ 0x03, 0x07, 0x05, 0x14 } /* Sharpest */
};
- cit_model3_Packet1(gspca_dev, 0x0060, sv[sd->sharpness].sv1);
- cit_model3_Packet1(gspca_dev, 0x0061, sv[sd->sharpness].sv2);
- cit_model3_Packet1(gspca_dev, 0x0062, sv[sd->sharpness].sv3);
- cit_model3_Packet1(gspca_dev, 0x0063, sv[sd->sharpness].sv4);
+ cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
+ cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
+ cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
+ cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
break;
}
}
@@ -1510,7 +1374,7 @@ static int cit_set_sharpness(struct gspca_dev *gspca_dev)
* 1/5/00 Created.
* 2/20/00 Added support for Model 2 cameras.
*/
-static void cit_set_lighting(struct gspca_dev *gspca_dev)
+static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1524,19 +1388,19 @@ static void cit_set_lighting(struct gspca_dev *gspca_dev)
case CIT_MODEL1: {
int i;
for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+ cit_Packet_Format1(gspca_dev, 0x0027, val);
break;
}
}
}
-static void cit_set_hflip(struct gspca_dev *gspca_dev)
+static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
switch (sd->model) {
case CIT_MODEL0:
- if (sd->hflip)
+ if (val)
cit_write_reg(gspca_dev, 0x0020, 0x0115);
else
cit_write_reg(gspca_dev, 0x0040, 0x0115);
@@ -1831,7 +1695,8 @@ static int cit_start_model1(struct gspca_dev *gspca_dev)
cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
/* Default lighting conditions */
- cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+ cit_Packet_Format1(gspca_dev, 0x0027,
+ v4l2_ctrl_g_ctrl(sd->lighting));
}
/* Assorted init */
@@ -2049,9 +1914,10 @@ static int cit_start_model2(struct gspca_dev *gspca_dev)
break;
}
- /* FIXME this cannot be changed while streaming, so we
- should report a grabbed flag for this control. */
- cit_model2_Packet1(gspca_dev, 0x0028, sd->lighting);
+ cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
+ /* model2 cannot change the backlight compensation while streaming */
+ v4l2_ctrl_grab(sd->lighting, true);
+
/* color balance rg2 */
cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
/* saturation */
@@ -2755,13 +2621,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
- cit_set_brightness(gspca_dev);
- cit_set_contrast(gspca_dev);
- cit_set_hue(gspca_dev);
- cit_set_sharpness(gspca_dev);
- cit_set_lighting(gspca_dev);
- cit_set_hflip(gspca_dev);
-
/* Program max isoc packet size */
cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
@@ -2857,6 +2716,8 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
break;
case CIT_MODEL2:
+ v4l2_ctrl_grab(sd->lighting, false);
+ /* Fall through! */
case CIT_MODEL4:
cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
@@ -3055,152 +2916,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_brightness(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
-
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
-
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_contrast(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
-
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
-
- return 0;
-}
-
-static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hue = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_hue(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
- return 0;
-}
-
-static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hue;
-
- return 0;
-}
-
-static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->sharpness = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_sharpness(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
- return 0;
-}
-
-static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->sharpness;
-
- return 0;
-}
-
-static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->lighting = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_lighting(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
- return 0;
-}
-
-static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->lighting;
-
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming) {
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- cit_set_hflip(gspca_dev);
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- }
- return 0;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hflip;
-
- return 0;
-}
-
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static void cit_check_button(struct gspca_dev *gspca_dev)
{
@@ -3234,13 +2949,117 @@ static void cit_check_button(struct gspca_dev *gspca_dev)
}
#endif
+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;
+
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cit_set_brightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ cit_set_contrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ cit_set_hue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ cit_set_hflip(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ cit_set_sharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ cit_set_lighting(gspca_dev, ctrl->val);
+ break;
+ }
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ 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 sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ bool has_brightness;
+ bool has_contrast;
+ bool has_hue;
+ bool has_sharpness;
+ bool has_lighting;
+ bool has_hflip;
+
+ has_brightness = has_contrast = has_hue =
+ has_sharpness = has_hflip = has_lighting = false;
+ switch (sd->model) {
+ case CIT_MODEL0:
+ has_contrast = has_hflip = true;
+ break;
+ case CIT_MODEL1:
+ has_brightness = has_contrast =
+ has_sharpness = has_lighting = true;
+ break;
+ case CIT_MODEL2:
+ has_brightness = has_hue = has_lighting = true;
+ break;
+ case CIT_MODEL3:
+ has_brightness = has_contrast = has_sharpness = true;
+ break;
+ case CIT_MODEL4:
+ has_brightness = has_hue = true;
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ has_brightness = has_hue =
+ has_sharpness = has_hflip = has_lighting = true;
+ break;
+ }
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 63, 1, 32);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 20, 1, 10);
+ if (has_hue)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 0, 127, 1, 63);
+ if (has_sharpness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 6, 1, 3);
+ if (has_lighting)
+ sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1);
+ if (has_hflip)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.stop0 = sd_stop0,
@@ -3253,10 +3072,9 @@ static const struct sd_desc sd_desc = {
static const struct sd_desc sd_desc_isoc_nego = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.isoc_init = sd_isoc_init,
.isoc_nego = sd_isoc_nego,
@@ -3320,6 +3138,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/ibmmpeg2.h b/drivers/media/video/ibmmpeg2.h
deleted file mode 100644
index 68e10387c498..000000000000
--- a/drivers/media/video/ibmmpeg2.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ibmmpeg2.h - IBM MPEGCD21 definitions */
-
-#ifndef __IBM_MPEG2__
-#define __IBM_MPEG2__
-
-/* Define all MPEG Decoder registers */
-/* Chip Control and Status */
-#define IBM_MP2_CHIP_CONTROL 0x200*2
-#define IBM_MP2_CHIP_MODE 0x201*2
-/* Timer Control and Status */
-#define IBM_MP2_SYNC_STC2 0x202*2
-#define IBM_MP2_SYNC_STC1 0x203*2
-#define IBM_MP2_SYNC_STC0 0x204*2
-#define IBM_MP2_SYNC_PTS2 0x205*2
-#define IBM_MP2_SYNC_PTS1 0x206*2
-#define IBM_MP2_SYNC_PTS0 0x207*2
-/* Video FIFO Control */
-#define IBM_MP2_FIFO 0x208*2
-#define IBM_MP2_FIFOW 0x100*2
-#define IBM_MP2_FIFO_STAT 0x209*2
-#define IBM_MP2_RB_THRESHOLD 0x22b*2
-/* Command buffer */
-#define IBM_MP2_COMMAND 0x20a*2
-#define IBM_MP2_CMD_DATA 0x20b*2
-#define IBM_MP2_CMD_STAT 0x20c*2
-#define IBM_MP2_CMD_ADDR 0x20d*2
-/* Internal Processor Control and Status */
-#define IBM_MP2_PROC_IADDR 0x20e*2
-#define IBM_MP2_PROC_IDATA 0x20f*2
-#define IBM_MP2_WR_PROT 0x235*2
-/* DRAM Access */
-#define IBM_MP2_DRAM_ADDR 0x210*2
-#define IBM_MP2_DRAM_DATA 0x212*2
-#define IBM_MP2_DRAM_CMD_STAT 0x213*2
-#define IBM_MP2_BLOCK_SIZE 0x23b*2
-#define IBM_MP2_SRC_ADDR 0x23c*2
-/* Onscreen Display */
-#define IBM_MP2_OSD_ADDR 0x214*2
-#define IBM_MP2_OSD_DATA 0x215*2
-#define IBM_MP2_OSD_MODE 0x217*2
-#define IBM_MP2_OSD_LINK_ADDR 0x229*2
-#define IBM_MP2_OSD_SIZE 0x22a*2
-/* Interrupt Control */
-#define IBM_MP2_HOST_INT 0x218*2
-#define IBM_MP2_MASK0 0x219*2
-#define IBM_MP2_HOST_INT1 0x23e*2
-#define IBM_MP2_MASK1 0x23f*2
-/* Audio Control */
-#define IBM_MP2_AUD_IADDR 0x21a*2
-#define IBM_MP2_AUD_IDATA 0x21b*2
-#define IBM_MP2_AUD_FIFO 0x21c*2
-#define IBM_MP2_AUD_FIFOW 0x101*2
-#define IBM_MP2_AUD_CTL 0x21d*2
-#define IBM_MP2_BEEP_CTL 0x21e*2
-#define IBM_MP2_FRNT_ATTEN 0x22d*2
-/* Display Control */
-#define IBM_MP2_DISP_MODE 0x220*2
-#define IBM_MP2_DISP_DLY 0x221*2
-#define IBM_MP2_VBI_CTL 0x222*2
-#define IBM_MP2_DISP_LBOR 0x223*2
-#define IBM_MP2_DISP_TBOR 0x224*2
-/* Polarity Control */
-#define IBM_MP2_INFC_CTL 0x22c*2
-
-/* control commands */
-#define IBM_MP2_PLAY 0
-#define IBM_MP2_PAUSE 1
-#define IBM_MP2_SINGLE_FRAME 2
-#define IBM_MP2_FAST_FORWARD 3
-#define IBM_MP2_SLOW_MOTION 4
-#define IBM_MP2_IMED_NORM_PLAY 5
-#define IBM_MP2_RESET_WINDOW 6
-#define IBM_MP2_FREEZE_FRAME 7
-#define IBM_MP2_RESET_VID_RATE 8
-#define IBM_MP2_CONFIG_DECODER 9
-#define IBM_MP2_CHANNEL_SWITCH 10
-#define IBM_MP2_RESET_AUD_RATE 11
-#define IBM_MP2_PRE_OP_CHN_SW 12
-#define IBM_MP2_SET_STILL_MODE 14
-
-/* Define Xilinx FPGA Internal Registers */
-
-/* general control register 0 */
-#define XILINX_CTL0 0x600
-/* genlock delay resister 1 */
-#define XILINX_GLDELAY 0x602
-/* send 16 bits to CS3310 port */
-#define XILINX_CS3310 0x604
-/* send 16 bits to CS3310 and complete */
-#define XILINX_CS3310_CMPLT 0x60c
-/* pulse width modulator control */
-#define XILINX_PWM 0x606
-
-#endif
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index f7d57b3f2842..32a591062d0b 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -1830,18 +1830,6 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio,
return 0;
}
-long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct video_device *vfd = video_devdata(filp);
- long ret;
-
- if (ivtv_debug & IVTV_DBGFLG_IOCTL)
- vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
- ret = video_ioctl2(filp, cmd, arg);
- vfd->debug = 0;
- return ret;
-}
-
static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
.vidioc_querycap = ivtv_querycap,
.vidioc_s_audio = ivtv_s_audio,
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h
index 89185caeafae..7c553d16579b 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.h
+++ b/drivers/media/video/ivtv/ivtv-ioctl.h
@@ -31,6 +31,5 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std);
void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std);
int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
-long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 6738592aa35d..87990c5f0910 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -50,7 +50,7 @@ static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
.open = ivtv_v4l2_open,
- .unlocked_ioctl = ivtv_v4l2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
.release = ivtv_v4l2_close,
.poll = ivtv_v4l2_enc_poll,
};
@@ -60,7 +60,7 @@ static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
.open = ivtv_v4l2_open,
- .unlocked_ioctl = ivtv_v4l2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
.release = ivtv_v4l2_close,
.poll = ivtv_v4l2_dec_poll,
};
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
index 302dc3d70193..dc8c2505907e 100644
--- a/drivers/media/video/m5mols/Kconfig
+++ b/drivers/media/video/m5mols/Kconfig
@@ -1,5 +1,6 @@
config VIDEO_M5MOLS
tristate "Fujitsu M-5MOLS 8MP sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
---help---
This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index 392a028730e2..fdbc205a2969 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -527,8 +527,8 @@ static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
/* Supported manual ISO values */
static const s64 iso_qmenu[] = {
- /* AE_ISO: 0x01...0x07 */
- 50, 100, 200, 400, 800, 1600, 3200
+ /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */
+ 50000, 100000, 200000, 400000, 800000, 1600000, 3200000
};
/* Supported Exposure Bias values, -2.0EV...+2.0EV */
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index 3945556f5733..7efe9ad7acc7 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -27,6 +27,8 @@
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
@@ -60,6 +62,10 @@ MODULE_VERSION("0.1.1");
#define MEM2MEM_COLOR_STEP (0xff >> 4)
#define MEM2MEM_NUM_TILES 8
+/* Flags that indicate processing mode */
+#define MEM2MEM_HFLIP (1 << 0)
+#define MEM2MEM_VFLIP (1 << 1)
+
#define dprintk(dev, fmt, arg...) \
v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
@@ -97,6 +103,8 @@ static struct m2mtest_fmt formats[] = {
},
};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
/* Per-queue, driver-specific private data */
struct m2mtest_q_data {
unsigned int width;
@@ -110,32 +118,8 @@ enum {
V4L2_M2M_DST = 1,
};
-#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE
-#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1)
-
-static struct v4l2_queryctrl m2mtest_ctrls[] = {
- {
- .id = V4L2_CID_TRANS_TIME_MSEC,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Transaction time (msec)",
- .minimum = 1,
- .maximum = 10000,
- .step = 100,
- .default_value = 1000,
- .flags = 0,
- }, {
- .id = V4L2_CID_TRANS_NUM_BUFS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Buffers per transaction",
- .minimum = 1,
- .maximum = MEM2MEM_DEF_NUM_BUFS,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
+#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
+#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
static struct m2mtest_fmt *find_format(struct v4l2_format *f)
{
@@ -168,8 +152,11 @@ struct m2mtest_dev {
};
struct m2mtest_ctx {
+ struct v4l2_fh fh;
struct m2mtest_dev *dev;
+ struct v4l2_ctrl_handler hdl;
+
/* Processed buffers in this transaction */
u8 num_processed;
@@ -181,12 +168,22 @@ struct m2mtest_ctx {
/* Abort requested by m2m */
int aborting;
+ /* Processing mode */
+ int mode;
+
+ enum v4l2_colorspace colorspace;
+
struct v4l2_m2m_ctx *m2m_ctx;
/* Source and destination queue data */
struct m2mtest_q_data q_data[2];
};
+static inline struct m2mtest_ctx *file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct m2mtest_ctx, fh);
+}
+
static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
enum v4l2_buf_type type)
{
@@ -202,18 +199,6 @@ static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
}
-static struct v4l2_queryctrl *get_ctrl(int id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) {
- if (id == m2mtest_ctrls[i].id)
- return &m2mtest_ctrls[i];
- }
-
- return NULL;
-}
-
static int device_process(struct m2mtest_ctx *ctx,
struct vb2_buffer *in_vb,
struct vb2_buffer *out_vb)
@@ -249,19 +234,84 @@ static int device_process(struct m2mtest_ctx *ctx,
bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
w = 0;
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP;
+ switch (ctx->mode) {
+ case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
+ p_out += bytesperline * height - bytes_left;
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
}
- ++w;
+ p_in += bytes_left;
+ p_out -= bytes_left;
+ }
+ break;
+
+ case MEM2MEM_HFLIP:
+ for (y = 0; y < height; ++y) {
+ p_out += MEM2MEM_NUM_TILES * tile_w;
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x01) {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytesperline;
+ }
+ break;
+
+ case MEM2MEM_VFLIP:
+ p_out += bytesperline * (height - 1);
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytes_left - 2 * bytesperline;
+ }
+ break;
+
+ default:
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytes_left;
}
- p_in += bytes_left;
- p_out += bytes_left;
}
return 0;
@@ -380,10 +430,9 @@ static int vidioc_querycap(struct file *file, void *priv,
{
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
-
+ strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -446,6 +495,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
f->fmt.pix.pixelformat = q_data->fmt->fourcc;
f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
f->fmt.pix.sizeimage = q_data->sizeimage;
+ f->fmt.pix.colorspace = ctx->colorspace;
return 0;
}
@@ -453,13 +503,13 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
- return vidioc_g_fmt(priv, f);
+ return vidioc_g_fmt(file2ctx(file), f);
}
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- return vidioc_g_fmt(priv, f);
+ return vidioc_g_fmt(file2ctx(file), f);
}
static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
@@ -498,7 +548,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
@@ -507,6 +557,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.pixelformat);
return -EINVAL;
}
+ f->fmt.pix.colorspace = ctx->colorspace;
return vidioc_try_fmt(f, fmt);
}
@@ -515,7 +566,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
@@ -524,6 +575,8 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
f->fmt.pix.pixelformat);
return -EINVAL;
}
+ if (!f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
return vidioc_try_fmt(f, fmt);
}
@@ -568,25 +621,29 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
if (ret)
return ret;
- return vidioc_s_fmt(priv, f);
+ return vidioc_s_fmt(file2ctx(file), f);
}
static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
+ struct m2mtest_ctx *ctx = file2ctx(file);
int ret;
ret = vidioc_try_fmt_vid_out(file, priv, f);
if (ret)
return ret;
- return vidioc_s_fmt(priv, f);
+ ret = vidioc_s_fmt(file2ctx(file), f);
+ if (!ret)
+ ctx->colorspace = f->fmt.pix.colorspace;
+ return ret;
}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
@@ -594,21 +651,21 @@ static int vidioc_reqbufs(struct file *file, void *priv,
static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
@@ -616,7 +673,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
@@ -624,79 +681,37 @@ static int vidioc_streamon(struct file *file, void *priv,
static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct v4l2_queryctrl *c;
-
- c = get_ctrl(qc->id);
- if (!c)
- return -EINVAL;
-
- *qc = *c;
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_ctx *ctx =
+ container_of(ctrl->handler, struct m2mtest_ctx, hdl);
switch (ctrl->id) {
- case V4L2_CID_TRANS_TIME_MSEC:
- ctrl->value = ctx->transtime;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ ctx->mode |= MEM2MEM_HFLIP;
+ else
+ ctx->mode &= ~MEM2MEM_HFLIP;
break;
- case V4L2_CID_TRANS_NUM_BUFS:
- ctrl->value = ctx->translen;
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ ctx->mode |= MEM2MEM_VFLIP;
+ else
+ ctx->mode &= ~MEM2MEM_VFLIP;
break;
- default:
- v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl)
-{
- struct v4l2_queryctrl *c;
-
- c = get_ctrl(ctrl->id);
- if (!c)
- return -EINVAL;
-
- if (ctrl->value < c->minimum || ctrl->value > c->maximum) {
- v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n");
- return -ERANGE;
- }
-
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct m2mtest_ctx *ctx = priv;
- int ret = 0;
-
- ret = check_ctrl_val(ctx, ctrl);
- if (ret != 0)
- return ret;
-
- switch (ctrl->id) {
case V4L2_CID_TRANS_TIME_MSEC:
- ctx->transtime = ctrl->value;
+ ctx->transtime = ctrl->val;
break;
case V4L2_CID_TRANS_NUM_BUFS:
- ctx->translen = ctrl->value;
+ ctx->translen = ctrl->val;
break;
default:
@@ -707,6 +722,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
return 0;
}
+static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
+ .s_ctrl = m2mtest_s_ctrl,
+};
+
static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
@@ -729,10 +748,8 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
-
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -844,6 +861,28 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
return vb2_queue_init(dst_vq);
}
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
+ .ops = &m2mtest_ctrl_ops,
+ .id = V4L2_CID_TRANS_TIME_MSEC,
+ .name = "Transaction Time (msec)",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 1001,
+ .min = 1,
+ .max = 10001,
+ .step = 100,
+};
+
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
+ .ops = &m2mtest_ctrl_ops,
+ .id = V4L2_CID_TRANS_NUM_BUFS,
+ .name = "Buffers Per Transaction",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 1,
+ .min = 1,
+ .max = MEM2MEM_DEF_NUM_BUFS,
+ .step = 1,
+};
+
/*
* File operations
*/
@@ -851,29 +890,51 @@ static int m2mtest_open(struct file *file)
{
struct m2mtest_dev *dev = video_drvdata(file);
struct m2mtest_ctx *ctx = NULL;
+ struct v4l2_ctrl_handler *hdl;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
- file->private_data = ctx;
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
ctx->dev = dev;
- ctx->translen = MEM2MEM_DEF_TRANSLEN;
- ctx->transtime = MEM2MEM_DEF_TRANSTIME;
- ctx->num_processed = 0;
+ hdl = &ctx->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
+ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ ctx->fh.ctrl_handler = hdl;
+ v4l2_ctrl_handler_setup(hdl);
ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
- ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+ ctx->q_data[V4L2_M2M_SRC].width = 640;
+ ctx->q_data[V4L2_M2M_SRC].height = 480;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage =
+ ctx->q_data[V4L2_M2M_SRC].width *
+ ctx->q_data[V4L2_M2M_SRC].height *
+ (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
+ ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
int ret = PTR_ERR(ctx->m2m_ctx);
+ v4l2_ctrl_handler_free(hdl);
kfree(ctx);
return ret;
}
+ v4l2_fh_add(&ctx->fh);
atomic_inc(&dev->num_inst);
dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
@@ -884,10 +945,13 @@ static int m2mtest_open(struct file *file)
static int m2mtest_release(struct file *file)
{
struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = file->private_data;
+ struct m2mtest_ctx *ctx = file2ctx(file);
dprintk(dev, "Releasing instance %p\n", ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_ctrl_handler_free(&ctx->hdl);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
kfree(ctx);
@@ -899,14 +963,14 @@ static int m2mtest_release(struct file *file)
static unsigned int m2mtest_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct m2mtest_ctx *ctx = file->private_data;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
}
static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct m2mtest_ctx *ctx = file->private_data;
+ struct m2mtest_ctx *ctx = file2ctx(file);
return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
}
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 7e648183f157..00583f5fd26b 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -22,7 +22,7 @@
/*
* mt9m001 i2c address 0x5d
- * The platform has to define ctruct i2c_board_info objects and link to them
+ * The platform has to define struct i2c_board_info objects and link to them
* from struct soc_camera_link
*/
diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c
index 3c1e626139b7..445359c96113 100644
--- a/drivers/media/video/mt9m032.c
+++ b/drivers/media/video/mt9m032.c
@@ -688,11 +688,17 @@ static const struct v4l2_subdev_ops mt9m032_ops = {
static int mt9m032_probe(struct i2c_client *client,
const struct i2c_device_id *devid)
{
+ struct mt9m032_platform_data *pdata = client->dev.platform_data;
struct i2c_adapter *adapter = client->adapter;
struct mt9m032 *sensor;
int chip_version;
int ret;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&client->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
@@ -708,7 +714,7 @@ static int mt9m032_probe(struct i2c_client *client,
mutex_init(&sensor->lock);
- sensor->pdata = client->dev.platform_data;
+ sensor->pdata = pdata;
v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -738,7 +744,7 @@ static int mt9m032_probe(struct i2c_client *client,
sensor->format.field = V4L2_FIELD_NONE;
sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
- v4l2_ctrl_handler_init(&sensor->ctrls, 4);
+ v4l2_ctrl_handler_init(&sensor->ctrls, 5);
v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
V4L2_CID_GAIN, 0, 127, 1, 64);
@@ -754,6 +760,9 @@ static int mt9m032_probe(struct i2c_client *client,
V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
MT9M032_SHUTTER_WIDTH_MAX, 1,
MT9M032_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, pdata->pix_clock,
+ pdata->pix_clock, 1, pdata->pix_clock);
if (sensor->ctrls.error) {
ret = sensor->ctrls.error;
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index b0c529964329..863d722dda06 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -214,7 +214,6 @@ struct mt9m111 {
int power_count;
const struct mt9m111_datafmt *fmt;
int lastpage; /* PageMap cache value */
- unsigned char datawidth;
};
/* Find a data format by a pixel code */
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
index 8f061d9ac443..3be537ef22d2 100644
--- a/drivers/media/video/mt9p031.c
+++ b/drivers/media/video/mt9p031.c
@@ -950,7 +950,7 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->model = did->driver_data;
mt9p031->reset = -1;
- v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
+ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5);
v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
@@ -963,6 +963,9 @@ static int mt9p031_probe(struct i2c_client *client,
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, pdata->target_freq,
+ pdata->target_freq, 1, pdata->target_freq);
for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c
index 49ca3cbfc6f1..6d343adf891d 100644
--- a/drivers/media/video/mt9t001.c
+++ b/drivers/media/video/mt9t001.c
@@ -691,7 +691,7 @@ static int mt9t001_video_probe(struct i2c_client *client)
return ret;
/* Configure the pixel clock polarity */
- if (pdata && pdata->clk_pol) {
+ if (pdata->clk_pol) {
ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
MT9T001_PIXEL_CLOCK_INVERT);
if (ret < 0)
@@ -715,10 +715,16 @@ static int mt9t001_video_probe(struct i2c_client *client)
static int mt9t001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
struct mt9t001 *mt9t001;
unsigned int i;
int ret;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&client->adapter->dev,
@@ -735,7 +741,7 @@ static int mt9t001_probe(struct i2c_client *client,
return -ENOMEM;
v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
- ARRAY_SIZE(mt9t001_gains) + 2);
+ ARRAY_SIZE(mt9t001_gains) + 3);
v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
@@ -743,6 +749,9 @@ static int mt9t001_probe(struct i2c_client *client,
MT9T001_SHUTTER_WIDTH_DEF);
v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk,
+ 1, pdata->ext_clk);
for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL);
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index bf63417adb8f..72479247522a 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -23,7 +23,7 @@
/*
* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
- * The platform has to define ctruct i2c_board_info objects and link to them
+ * The platform has to define struct i2c_board_info objects and link to them
* from struct soc_camera_link
*/
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
index 0bd5815de369..5f8a6f5b98f9 100644
--- a/drivers/media/video/mx2_emmaprp.c
+++ b/drivers/media/video/mx2_emmaprp.c
@@ -396,9 +396,13 @@ static int vidioc_querycap(struct file *file, void *priv,
{
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
-
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
return 0;
}
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 7e32331b60fb..f1220d3d4970 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -2014,7 +2014,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
return -EINVAL;
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = INT_MAX;
@@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
ccdc_try_crop(ccdc, format, &sel->r);
break;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
break;
@@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
- if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != CCDC_PAD_SOURCE_OF)
return -EINVAL;
@@ -2064,7 +2064,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
* pad. If the KEEP_CONFIG flag is set, just return the current crop
* rectangle.
*/
- if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
return 0;
}
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index dd91da26f1b0..53f5a703e31a 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -1949,7 +1949,7 @@ static int preview_get_selection(struct v4l2_subdev *sd,
return -EINVAL;
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = INT_MAX;
@@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd,
preview_try_crop(prev, format, &sel->r);
break;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
sel->r = *__preview_get_crop(prev, fh, sel->which);
break;
@@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd,
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
- if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != PREV_PAD_SINK)
return -EINVAL;
@@ -2000,7 +2000,7 @@ static int preview_set_selection(struct v4l2_subdev *sd,
* pad. If the KEEP_CONFIG flag is set, just return the current crop
* rectangle.
*/
- if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
sel->r = *__preview_get_crop(prev, fh, sel->which);
return 0;
}
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 14041c9c8643..ae17d917f77b 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -1249,7 +1249,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd,
sel->which);
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = INT_MAX;
@@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd,
resizer_calc_ratios(res, &sel->r, format_source, &ratio);
break;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
sel->r = *__resizer_get_crop(res, fh, sel->which);
resizer_calc_ratios(res, &sel->r, format_source, &ratio);
break;
@@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *format_sink, *format_source;
struct resizer_ratio ratio;
- if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ if (sel->target != V4L2_SEL_TGT_CROP ||
sel->pad != RESZ_PAD_SINK)
return -EINVAL;
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index 3c2c5d3bcc6b..7c44d1fe3c87 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -837,10 +837,8 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd,
if (!priv->win) {
u32 width = W_SVGA, height = H_SVGA;
- int ret = ov2640_set_params(client, &width, &height,
- V4L2_MBUS_FMT_UYVY8_2X8);
- if (ret < 0)
- return ret;
+ priv->win = ov2640_select_win(&width, &height);
+ priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8;
}
mf->width = priv->win->width;
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 74e77d327ed8..6d79b89b8603 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -880,15 +880,11 @@ static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
static int ov772x_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
if (!priv->win || !priv->cfmt) {
- u32 width = VGA_WIDTH, height = VGA_HEIGHT;
- int ret = ov772x_set_params(client, &width, &height,
- V4L2_MBUS_FMT_YUYV8_2X8);
- if (ret < 0)
- return ret;
+ priv->cfmt = &ov772x_cfmts[0];
+ priv->win = ov772x_select_win(VGA_WIDTH, VGA_HEIGHT);
}
mf->width = priv->win->width;
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index 23412debb36b..9ed4ba4236c4 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -605,6 +605,7 @@ static int ov9640_video_probe(struct i2c_client *client)
devname = "ov9640";
priv->model = V4L2_IDENT_OV9640;
priv->revision = 2;
+ break;
case OV9640_V3:
devname = "ov9640";
priv->model = V4L2_IDENT_OV9640;
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index b4c679b3fb0f..77f9c92186f4 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -30,7 +30,6 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/mutex.h>
-#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/isa.h>
#include <asm/io.h>
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index f9b6001e1dd7..25e412ecad2c 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_PVRUSB2
tristate "Hauppauge WinTV-PVR USB2 support"
depends on VIDEO_V4L2 && I2C
- depends on VIDEO_MEDIA # Avoids pvrusb = Y / DVB = M
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 7bddfaeeafc3..f344aed32a93 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -226,13 +226,11 @@ static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
struct v4l2_input tmp;
unsigned int cnt;
int val;
- int ret;
cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
memset(&tmp, 0, sizeof(tmp));
tmp.index = vi->index;
- ret = 0;
if (vi->index >= fh->input_cnt)
return -EINVAL;
val = fh->input_map[vi->index];
@@ -556,9 +554,7 @@ static int pvr2_queryctrl(struct file *file, void *priv,
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct pvr2_ctrl *cptr;
int val;
- int ret;
- ret = 0;
if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
cptr = pvr2_hdw_get_ctrl_nextv4l(
hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
@@ -705,11 +701,9 @@ static int pvr2_try_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_control *ctrl;
struct pvr2_ctrl *pctl;
unsigned int idx;
- int ret;
/* For the moment just validate that the requested control
actually exists. */
- ret = 0;
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
@@ -770,12 +764,10 @@ static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct v4l2_cropcap cap;
int ret;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
crop->c.left);
@@ -965,7 +957,7 @@ static long pvr2_v4l2_ioctl(struct file *file,
long ret = -EINVAL;
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
- v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
+ v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
if (!pvr2_hdw_dev_ok(hdw)) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
@@ -998,7 +990,7 @@ static long pvr2_v4l2_ioctl(struct file *file,
pvr2_trace(PVR2_TRACE_V4LIOCTL,
"pvr2_v4l2_do_ioctl failure, ret=%ld"
" command was:", ret);
- v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
+ v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw),
cmd);
}
}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index ec4e2ef54e65..de7c7ba99ef4 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -136,19 +136,13 @@ static int leds[2] = { 100, 0 };
/***/
-static int pwc_video_close(struct file *file);
-static ssize_t pwc_video_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos);
-static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
-static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
-
static const struct v4l2_file_operations pwc_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
- .release = pwc_video_close,
- .read = pwc_video_read,
- .poll = pwc_video_poll,
- .mmap = pwc_video_mmap,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
static struct video_device pwc_template = {
@@ -562,17 +556,6 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
/***************************************************************************/
/* Video4Linux functions */
-int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
-{
- if (pdev->capt_file != NULL &&
- pdev->capt_file != file)
- return -EBUSY;
-
- pdev->capt_file = file;
-
- return 0;
-}
-
static void pwc_video_release(struct v4l2_device *v)
{
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
@@ -583,113 +566,6 @@ static void pwc_video_release(struct v4l2_device *v)
kfree(pdev);
}
-static int pwc_video_close(struct file *file)
-{
- struct pwc_device *pdev = video_drvdata(file);
-
- /*
- * If we're still streaming vb2_queue_release will call stream_stop
- * so we must take both the v4l2_lock and the vb_queue_lock.
- */
- if (mutex_lock_interruptible(&pdev->v4l2_lock))
- return -ERESTARTSYS;
- if (mutex_lock_interruptible(&pdev->vb_queue_lock)) {
- mutex_unlock(&pdev->v4l2_lock);
- return -ERESTARTSYS;
- }
-
- if (pdev->capt_file == file) {
- vb2_queue_release(&pdev->vb_queue);
- pdev->capt_file = NULL;
- }
-
- mutex_unlock(&pdev->vb_queue_lock);
- mutex_unlock(&pdev->v4l2_lock);
-
- return v4l2_fh_release(file);
-}
-
-static ssize_t pwc_video_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int lock_v4l2 = 0;
- ssize_t ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret)
- goto out;
-
- /* stream_start will get called so we must take the v4l2_lock */
- if (pdev->vb_queue.fileio == NULL)
- lock_v4l2 = 1;
-
- /* Use try_lock, since we're taking the locks in the *wrong* order! */
- if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) {
- ret = -ERESTARTSYS;
- goto out;
- }
- ret = vb2_read(&pdev->vb_queue, buf, count, ppos,
- file->f_flags & O_NONBLOCK);
- if (lock_v4l2)
- mutex_unlock(&pdev->v4l2_lock);
-out:
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
-{
- struct pwc_device *pdev = video_drvdata(file);
- struct vb2_queue *q = &pdev->vb_queue;
- unsigned long req_events = poll_requested_events(wait);
- unsigned int ret = POLL_ERR;
- int lock_v4l2 = 0;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return POLL_ERR;
-
- /* Will this start fileio and thus call start_stream? */
- if ((req_events & (POLLIN | POLLRDNORM)) &&
- q->num_buffers == 0 && !q->streaming && q->fileio == NULL) {
- if (pwc_test_n_set_capt_file(pdev, file))
- goto out;
- lock_v4l2 = 1;
- }
-
- /* Use try_lock, since we're taking the locks in the *wrong* order! */
- if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock))
- goto out;
- ret = vb2_poll(&pdev->vb_queue, file, wait);
- if (lock_v4l2)
- mutex_unlock(&pdev->v4l2_lock);
-
-out:
- if (!pdev->udev)
- ret |= POLLHUP;
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_mmap(&pdev->vb_queue, vma);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
/***************************************************************************/
/* Videobuf2 operations */
@@ -782,6 +658,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
if (!pdev->udev)
return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ return -ERESTARTSYS;
/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
pwc_set_leds(pdev, leds[0], leds[1]);
@@ -794,6 +672,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
/* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev);
}
+ mutex_unlock(&pdev->v4l2_lock);
return r;
}
@@ -802,6 +681,8 @@ static int stop_streaming(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ return -ERESTARTSYS;
if (pdev->udev) {
pwc_set_leds(pdev, 0, 0);
pwc_camera_power(pdev, 0);
@@ -809,22 +690,11 @@ static int stop_streaming(struct vb2_queue *vq)
}
pwc_cleanup_queued_bufs(pdev);
+ mutex_unlock(&pdev->v4l2_lock);
return 0;
}
-static void wait_prepare(struct vb2_queue *vq)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- mutex_unlock(&pdev->vb_queue_lock);
-}
-
-static void wait_finish(struct vb2_queue *vq)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- mutex_lock(&pdev->vb_queue_lock);
-}
-
static struct vb2_ops pwc_vb_queue_ops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
@@ -834,8 +704,8 @@ static struct vb2_ops pwc_vb_queue_ops = {
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
- .wait_prepare = wait_prepare,
- .wait_finish = wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
/***************************************************************************/
@@ -1136,6 +1006,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
/* Init video_device structure */
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
strcpy(pdev->vdev.name, name);
+ pdev->vdev.queue = &pdev->vb_queue;
+ pdev->vdev.queue->lock = &pdev->vb_queue_lock;
set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
video_set_drvdata(&pdev->vdev, pdev);
@@ -1190,15 +1062,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
pdev->vdev.lock = &pdev->v4l2_lock;
- /*
- * Don't take v4l2_lock for these ioctls. This improves latency if
- * v4l2_lock is taken for a long time, e.g. when changing a control
- * value, and a new frame is ready to be dequeued.
- */
- v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF);
- v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF);
- v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF);
-
rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
if (rc < 0) {
PWC_ERROR("Failed to register as video device (%d).\n", rc);
@@ -1253,20 +1116,18 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
struct v4l2_device *v = usb_get_intfdata(intf);
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
- mutex_lock(&pdev->v4l2_lock);
-
mutex_lock(&pdev->vb_queue_lock);
+ mutex_lock(&pdev->v4l2_lock);
/* No need to keep the urbs around after disconnection */
if (pdev->vb_queue.streaming)
pwc_isoc_cleanup(pdev);
pdev->udev = NULL;
pwc_cleanup_queued_bufs(pdev);
- mutex_unlock(&pdev->vb_queue_lock);
v4l2_device_disconnect(&pdev->v4l2_dev);
video_unregister_device(&pdev->vdev);
-
mutex_unlock(&pdev->v4l2_lock);
+ mutex_unlock(pdev->vb_queue.lock);
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
if (pdev->button_dev)
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index c691e29cc36e..545e9bbdeede 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -405,6 +405,7 @@ static void pwc_vidioc_fill_fmt(struct v4l2_format *f,
f->fmt.pix.pixelformat = pixfmt;
f->fmt.pix.bytesperline = f->fmt.pix.width;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() "
"width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
f->fmt.pix.width,
@@ -468,17 +469,8 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
if (ret < 0)
return ret;
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret)
- goto leave;
-
- if (pdev->vb_queue.streaming) {
- ret = -EBUSY;
- goto leave;
- }
+ if (vb2_is_busy(&pdev->vb_queue))
+ return -EBUSY;
pixelformat = f->fmt.pix.pixelformat;
@@ -496,8 +488,6 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
-leave:
- mutex_unlock(&pdev->vb_queue_lock);
return ret;
}
@@ -508,10 +498,9 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap
strcpy(cap->driver, PWC_NAME);
strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -520,7 +509,8 @@ static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
if (i->index) /* Only one INPUT is supported */
return -EINVAL;
- strcpy(i->name, "usb");
+ strlcpy(i->name, "Camera", sizeof(i->name));
+ i->type = V4L2_INPUT_TYPE_CAMERA;
return 0;
}
@@ -933,104 +923,6 @@ static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *
return pwc_vidioc_try_fmt(pdev, f);
}
-static int pwc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *rb)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_reqbufs(&pdev->vb_queue, rb);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_querybuf(&pdev->vb_queue, buf);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_qbuf(&pdev->vb_queue, buf);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_dqbuf(&pdev->vb_queue, buf,
- file->f_flags & O_NONBLOCK);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_streamon(&pdev->vb_queue, i);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
-static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct pwc_device *pdev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
-
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret == 0)
- ret = vb2_streamoff(&pdev->vb_queue, i);
-
- mutex_unlock(&pdev->vb_queue_lock);
- return ret;
-}
-
static int pwc_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
@@ -1112,32 +1004,27 @@ static int pwc_s_parm(struct file *file, void *fh,
int compression = 0;
int ret, fps;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- parm->parm.capture.timeperframe.numerator == 0)
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- fps = parm->parm.capture.timeperframe.denominator /
- parm->parm.capture.timeperframe.numerator;
-
- if (mutex_lock_interruptible(&pdev->vb_queue_lock))
- return -ERESTARTSYS;
+ /* If timeperframe == 0, then reset the framerate to the nominal value.
+ We pick a high framerate here, and let pwc_set_video_mode() figure
+ out the best match. */
+ if (parm->parm.capture.timeperframe.numerator == 0 ||
+ parm->parm.capture.timeperframe.denominator == 0)
+ fps = 30;
+ else
+ fps = parm->parm.capture.timeperframe.denominator /
+ parm->parm.capture.timeperframe.numerator;
- ret = pwc_test_n_set_capt_file(pdev, file);
- if (ret)
- goto leave;
-
- if (pdev->vb_queue.streaming) {
- ret = -EBUSY;
- goto leave;
- }
+ if (vb2_is_busy(&pdev->vb_queue))
+ return -EBUSY;
ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
fps, &compression, 0);
pwc_g_parm(file, fh, parm);
-leave:
- mutex_unlock(&pdev->vb_queue_lock);
return ret;
}
@@ -1150,12 +1037,12 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap,
- .vidioc_reqbufs = pwc_reqbufs,
- .vidioc_querybuf = pwc_querybuf,
- .vidioc_qbuf = pwc_qbuf,
- .vidioc_dqbuf = pwc_dqbuf,
- .vidioc_streamon = pwc_streamon,
- .vidioc_streamoff = pwc_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_enum_framesizes = pwc_enum_framesizes,
.vidioc_enum_frameintervals = pwc_enum_frameintervals,
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index d6b5b216b9d6..7a6a0d39c2c6 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -239,7 +239,6 @@ struct pwc_device
int features; /* feature bits */
/*** Video data ***/
- struct file *capt_file; /* file doing video capture */
int vendpoint; /* video isoc endpoint */
int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */
@@ -355,8 +354,6 @@ struct pwc_device
extern int pwc_trace;
#endif
-int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file);
-
/** Functions in pwc-misc.c */
/* sizes in pixels */
extern const int pwc_image_sizes[PSZ_MAX][2];
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 01c2179f0520..95007dda0c93 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -2686,3 +2686,4 @@ MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
MODULE_LICENSE("GPL");
MODULE_VERSION(S2255_VERSION);
+MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 725812aa0c30..8e413dd3c0b0 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -480,48 +480,59 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc);
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
- int ret;
+ int ret = -EBUSY;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
if (fimc_m2m_active(fimc))
- return -EBUSY;
+ goto unlock;
set_bit(ST_CAPT_BUSY, &fimc->state);
ret = pm_runtime_get_sync(&fimc->pdev->dev);
if (ret < 0)
- return ret;
+ goto unlock;
ret = v4l2_fh_open(file);
- if (ret)
- return ret;
-
- if (++fimc->vid_cap.refcnt != 1)
- return 0;
+ if (ret) {
+ pm_runtime_put(&fimc->pdev->dev);
+ goto unlock;
+ }
- ret = fimc_pipeline_initialize(&fimc->pipeline,
+ if (++fimc->vid_cap.refcnt == 1) {
+ ret = fimc_pipeline_initialize(&fimc->pipeline,
&fimc->vid_cap.vfd->entity, true);
- if (ret < 0) {
- clear_bit(ST_CAPT_BUSY, &fimc->state);
- pm_runtime_put_sync(&fimc->pdev->dev);
- fimc->vid_cap.refcnt--;
- v4l2_fh_release(file);
- return ret;
- }
- ret = fimc_capture_ctrls_create(fimc);
- if (!ret && !fimc->vid_cap.user_subdev_api)
- ret = fimc_capture_set_default_format(fimc);
+ if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_capture_set_default_format(fimc);
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+
+ if (ret < 0) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ fimc->vid_cap.refcnt--;
+ v4l2_fh_release(file);
+ }
+ }
+unlock:
+ mutex_unlock(&fimc->lock);
return ret;
}
static int fimc_capture_close(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
if (--fimc->vid_cap.refcnt == 0) {
clear_bit(ST_CAPT_BUSY, &fimc->state);
fimc_stop_capture(fimc, false);
@@ -535,22 +546,40 @@ static int fimc_capture_close(struct file *file)
vb2_queue_release(&fimc->vid_cap.vbq);
fimc_ctrls_delete(fimc->vid_cap.ctx);
}
- return v4l2_fh_release(file);
+
+ ret = v4l2_fh_release(file);
+
+ mutex_unlock(&fimc->lock);
+ return ret;
}
static unsigned int fimc_capture_poll(struct file *file,
struct poll_table_struct *wait)
{
struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
- return vb2_poll(&fimc->vid_cap.vbq, file, wait);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return POLL_ERR;
+
+ ret = vb2_poll(&fimc->vid_cap.vbq, file, wait);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
}
static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
{
struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
- return vb2_mmap(&fimc->vid_cap.vbq, vma);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = vb2_mmap(&fimc->vid_cap.vbq, vma);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
}
static const struct v4l2_file_operations fimc_capture_fops = {
@@ -658,7 +687,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
r->left = r->top = 0;
return;
}
- if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+ if (target == V4L2_SEL_TGT_COMPOSE) {
if (ctx->rotation != 90 && ctx->rotation != 270)
align_h = 1;
max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
@@ -685,7 +714,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
rotate ? sink->f_height : sink->f_width);
max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
- if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+ if (target == V4L2_SEL_TGT_COMPOSE) {
min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
if (rotate) {
@@ -1146,9 +1175,9 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
s->r.height = f->o_height;
return 0;
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE:
f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP_ACTIVE:
+ case V4L2_SEL_TGT_CROP:
s->r.left = f->offs_h;
s->r.top = f->offs_v;
s->r.width = f->width;
@@ -1160,7 +1189,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh,
}
/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
{
if (a->left < b->left || a->top < b->top)
return 0;
@@ -1184,9 +1213,9 @@ static int fimc_cap_s_selection(struct file *file, void *fh,
if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE)
+ if (s->target == V4L2_SEL_TGT_COMPOSE)
f = &ctx->d_frame;
- else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE)
+ else if (s->target == V4L2_SEL_TGT_CROP)
f = &ctx->s_frame;
else
return -EINVAL;
@@ -1428,9 +1457,9 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
mutex_lock(&fimc->lock);
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
r->width = f->o_width;
r->height = f->o_height;
r->left = 0;
@@ -1438,10 +1467,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
mutex_unlock(&fimc->lock);
return 0;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
break;
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SEL_TGT_COMPOSE:
try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
f = &ctx->d_frame;
break;
@@ -1482,12 +1511,12 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
return -EINVAL;
mutex_lock(&fimc->lock);
- fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE);
+ fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
f = &ctx->d_frame;
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
r->width = f->o_width;
r->height = f->o_height;
r->left = 0;
@@ -1495,10 +1524,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
mutex_unlock(&fimc->lock);
return 0;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
break;
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SEL_TGT_COMPOSE:
try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
f = &ctx->d_frame;
break;
@@ -1514,7 +1543,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
set_frame_crop(f, r->left, r->top, r->width, r->height);
set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
spin_unlock_irqrestore(&fimc->slock, flags);
- if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL)
+ if (sel->target == V4L2_SEL_TGT_COMPOSE)
ctx->state |= FIMC_COMPOSE;
}
@@ -1589,10 +1618,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
+
video_set_drvdata(vfd, fimc);
vid_cap = &fimc->vid_cap;
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index a4646ca1d56f..1a445404e73d 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -463,7 +463,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v);
}
-int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)
+static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)
{
struct fimc_effect *effect = &ctx->effect;
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 95b27ae5cf27..808ccc621846 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -27,9 +27,6 @@
#include <media/v4l2-mediabus.h>
#include <media/s5p_fimc.h>
-#define err(fmt, args...) \
- printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
-
#define dbg(fmt, args...) \
pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c
index 419adfb7cdf9..f996e94873f6 100644
--- a/drivers/media/video/s5p-fimc/fimc-lite-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.c
@@ -215,7 +215,7 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev,
flite_hw_set_camera_port(dev, s_info->mux_id);
}
-void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
+static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
{
static const u32 pixcode[4][2] = {
{ V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c
index 74ff310db30c..c5b57e805b68 100644
--- a/drivers/media/video/s5p-fimc/fimc-lite.c
+++ b/drivers/media/video/s5p-fimc/fimc-lite.c
@@ -902,7 +902,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh,
sel->r.height = f->f_height;
return 0;
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE:
sel->r = f->rect;
return 0;
}
@@ -919,7 +919,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh,
unsigned long flags;
if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
- sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE)
+ sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
fimc_lite_try_compose(fimc, &rect);
@@ -1117,9 +1117,9 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
struct flite_frame *f = &fimc->inp_frame;
- if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL &&
- sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) ||
- sel->pad != FLITE_SD_PAD_SINK)
+ if ((sel->target != V4L2_SEL_TGT_CROP &&
+ sel->target != V4L2_SEL_TGT_CROP_BOUNDS) ||
+ sel->pad != FLITE_SD_PAD_SINK)
return -EINVAL;
if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
@@ -1128,7 +1128,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
}
mutex_lock(&fimc->lock);
- if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) {
+ if (sel->target == V4L2_SEL_TGT_CROP) {
sel->r = f->rect;
} else {
sel->r.left = 0;
@@ -1153,8 +1153,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
struct flite_frame *f = &fimc->inp_frame;
int ret = 0;
- if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
- sel->pad != FLITE_SD_PAD_SINK)
+ if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK)
return -EINVAL;
mutex_lock(&fimc->lock);
diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c
index 4c58e0570962..c587011d80ef 100644
--- a/drivers/media/video/s5p-fimc/fimc-m2m.c
+++ b/drivers/media/video/s5p-fimc/fimc-m2m.c
@@ -259,7 +259,12 @@ static int fimc_m2m_querycap(struct file *file, void *fh,
strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING |
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
@@ -642,21 +647,25 @@ static int fimc_m2m_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_ctx *ctx;
- int ret;
+ int ret = -EBUSY;
dbg("pid: %d, state: 0x%lx, refcnt: %d",
task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
/*
* Return if the corresponding video capture node
* is already opened.
*/
if (fimc->vid_cap.refcnt > 0)
- return -EBUSY;
+ goto unlock;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
ctx->fimc_dev = fimc;
@@ -687,6 +696,8 @@ static int fimc_m2m_open(struct file *file)
if (fimc->m2m.refcnt++ == 0)
set_bit(ST_M2M_RUN, &fimc->state);
+
+ mutex_unlock(&fimc->lock);
return 0;
error_c:
@@ -695,6 +706,8 @@ error_fh:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
+unlock:
+ mutex_unlock(&fimc->lock);
return ret;
}
@@ -706,6 +719,9 @@ static int fimc_m2m_release(struct file *file)
dbg("pid: %d, state: 0x%lx, refcnt= %d",
task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
v4l2_m2m_ctx_release(ctx->m2m_ctx);
fimc_ctrls_delete(ctx);
v4l2_fh_del(&ctx->fh);
@@ -714,6 +730,8 @@ static int fimc_m2m_release(struct file *file)
if (--fimc->m2m.refcnt <= 0)
clear_bit(ST_M2M_RUN, &fimc->state);
kfree(ctx);
+
+ mutex_unlock(&fimc->lock);
return 0;
}
@@ -721,16 +739,32 @@ static unsigned int fimc_m2m_poll(struct file *file,
struct poll_table_struct *wait)
{
struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&fimc->lock);
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ return ret;
}
static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
{
struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
}
static const struct v4l2_file_operations fimc_m2m_fops = {
@@ -772,10 +806,6 @@ int fimc_register_m2m_device(struct fimc_dev *fimc,
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
video_set_drvdata(vfd, fimc);
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
index 52cef4865423..e65bb283fd8a 100644
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(fimc_pipeline_initialize);
* sensor clock.
* Called with the graph mutex held.
*/
-int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
+static int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
{
int ret = 0;
@@ -1010,7 +1010,7 @@ static struct platform_driver fimc_md_driver = {
}
};
-int __init fimc_md_init(void)
+static int __init fimc_md_init(void)
{
int ret;
@@ -1021,7 +1021,8 @@ int __init fimc_md_init(void)
return platform_driver_register(&fimc_md_driver);
}
-void __exit fimc_md_exit(void)
+
+static void __exit fimc_md_exit(void)
{
platform_driver_unregister(&fimc_md_driver);
fimc_unregister_driver();
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 1fc4ce8446f5..0e3eb9ce4f98 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -667,7 +667,8 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB |
FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG);
- if (cam->bus_type == FIMC_MIPI_CSI2) {
+ switch (cam->bus_type) {
+ case FIMC_MIPI_CSI2:
cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI;
if (cam->mux_id == 0)
@@ -683,23 +684,24 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
default:
- v4l2_err(fimc->vid_cap.vfd,
- "Not supported camera pixel format: %d",
+ v4l2_err(vid_cap->vfd,
+ "Not supported camera pixel format: %#x\n",
vid_cap->mf.code);
return -EINVAL;
}
tmp |= (csis_data_alignment == 32) << 8;
writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT);
-
- } else if (cam->bus_type == FIMC_ITU_601 ||
- cam->bus_type == FIMC_ITU_656) {
+ break;
+ case FIMC_ITU_601...FIMC_ITU_656:
if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A;
- } else if (cam->bus_type == FIMC_LCD_WB) {
+ break;
+ case FIMC_LCD_WB:
cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
- } else {
- err("invalid camera bus type selected\n");
+ break;
+ default:
+ v4l2_err(vid_cap->vfd, "Invalid camera bus type selected\n");
return -EINVAL;
}
writel(cfg, fimc->regs + FIMC_REG_CIGCTRL);
diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c
index 7c98ee7377ee..7c2200435206 100644
--- a/drivers/media/video/s5p-g2d/g2d.c
+++ b/drivers/media/video/s5p-g2d/g2d.c
@@ -290,8 +290,13 @@ static int vidioc_querycap(struct file *file, void *priv,
strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
return 0;
}
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c
index 28b5225d94f5..813b801238d1 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.c
@@ -489,9 +489,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv,
sizeof(cap->card));
}
cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OUTPUT;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
return 0;
}
@@ -824,10 +828,10 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv,
/* For JPEG blob active == default == bounds */
switch (s->target) {
- case V4L2_SEL_TGT_CROP_ACTIVE:
+ case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
s->r.width = ctx->out_q.w;
s->r.height = ctx->out_q.h;
@@ -1503,29 +1507,7 @@ static struct platform_driver s5p_jpeg_driver = {
},
};
-static int __init
-s5p_jpeg_register(void)
-{
- int ret;
-
- pr_info("S5P JPEG V4L2 Driver, (c) 2011 Samsung Electronics\n");
-
- ret = platform_driver_register(&s5p_jpeg_driver);
-
- if (ret)
- pr_err("%s: failed to register jpeg driver\n", __func__);
-
- return ret;
-}
-
-static void __exit
-s5p_jpeg_unregister(void)
-{
- platform_driver_unregister(&s5p_jpeg_driver);
-}
-
-module_init(s5p_jpeg_register);
-module_exit(s5p_jpeg_unregister);
+module_platform_driver(s5p_jpeg_driver);
MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
MODULE_DESCRIPTION("Samsung JPEG codec driver");
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
index feea867f318c..c5d567f87d77 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -220,8 +220,14 @@ static int vidioc_querycap(struct file *file, void *priv,
strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
index 158b78989b89..aa1c244cf66e 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -779,9 +779,14 @@ static int vidioc_querycap(struct file *file, void *priv,
strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE
- | V4L2_CAP_VIDEO_OUTPUT_MPLANE
- | V4L2_CAP_STREAMING;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE;
return 0;
}
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
index 33fde2a763ec..6c74b05d1f95 100644
--- a/drivers/media/video/s5p-tv/mixer_video.c
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -367,7 +367,7 @@ static int mxr_g_selection(struct file *file, void *fh,
return -EINVAL;
switch (s->target) {
- case V4L2_SEL_TGT_CROP_ACTIVE:
+ case V4L2_SEL_TGT_CROP:
s->r.left = geo->src.x_offset;
s->r.top = geo->src.y_offset;
s->r.width = geo->src.width;
@@ -380,7 +380,7 @@ static int mxr_g_selection(struct file *file, void *fh,
s->r.width = geo->src.full_width;
s->r.height = geo->src.full_height;
break;
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_PADDED:
s->r.left = geo->dst.x_offset;
s->r.top = geo->dst.y_offset;
@@ -449,11 +449,11 @@ static int mxr_s_selection(struct file *file, void *fh,
res.height = geo->dst.full_height;
break;
- case V4L2_SEL_TGT_CROP_ACTIVE:
+ case V4L2_SEL_TGT_CROP:
target = &geo->src;
stage = MXR_GEOMETRY_CROP;
break;
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_PADDED:
target = &geo->dst;
stage = MXR_GEOMETRY_COMPOSE;
diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c
index 0f31eccd7b80..6d348f90237a 100644
--- a/drivers/media/video/s5p-tv/sii9234_drv.c
+++ b/drivers/media/video/s5p-tv/sii9234_drv.c
@@ -419,14 +419,4 @@ static struct i2c_driver sii9234_driver = {
.id_table = sii9234_id,
};
-static int __init sii9234_init(void)
-{
- return i2c_add_driver(&sii9234_driver);
-}
-module_init(sii9234_init);
-
-static void __exit sii9234_exit(void)
-{
- i2c_del_driver(&sii9234_driver);
-}
-module_exit(sii9234_exit);
+module_i2c_driver(sii9234_driver);
diff --git a/drivers/media/video/saa7121.h b/drivers/media/video/saa7121.h
deleted file mode 100644
index 66967ae37494..000000000000
--- a/drivers/media/video/saa7121.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* saa7121.h - saa7121 initializations
- Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-#ifndef __SAA7121_H__
-#define __SAA7121_H__
-
-#define NTSC_BURST_START 0x19 /* 28 */
-#define NTSC_BURST_END 0x1d /* 29 */
-#define NTSC_CHROMA_PHASE 0x67 /* 5a */
-#define NTSC_GAINU 0x76 /* 5b */
-#define NTSC_GAINV 0xa5 /* 5c */
-#define NTSC_BLACK_LEVEL 0x2a /* 5d */
-#define NTSC_BLANKING_LEVEL 0x2e /* 5e */
-#define NTSC_VBI_BLANKING 0x2e /* 5f */
-#define NTSC_DAC_CONTROL 0x11 /* 61 */
-#define NTSC_BURST_AMP 0x3f /* 62 */
-#define NTSC_SUBC3 0x1f /* 63 */
-#define NTSC_SUBC2 0x7c /* 64 */
-#define NTSC_SUBC1 0xf0 /* 65 */
-#define NTSC_SUBC0 0x21 /* 66 */
-#define NTSC_HTRIG 0x72 /* 6c */
-#define NTSC_VTRIG 0x00 /* 6c */
-#define NTSC_MULTI 0x30 /* 6e */
-#define NTSC_CCTTX 0x11 /* 6f */
-#define NTSC_FIRST_ACTIVE 0x12 /* 7a */
-#define NTSC_LAST_ACTIVE 0x02 /* 7b */
-#define NTSC_MSB_VERTICAL 0x40 /* 7c */
-
-#define PAL_BURST_START 0x21 /* 28 */
-#define PAL_BURST_END 0x1d /* 29 */
-#define PAL_CHROMA_PHASE 0x3f /* 5a */
-#define PAL_GAINU 0x7d /* 5b */
-#define PAL_GAINV 0xaf /* 5c */
-#define PAL_BLACK_LEVEL 0x23 /* 5d */
-#define PAL_BLANKING_LEVEL 0x35 /* 5e */
-#define PAL_VBI_BLANKING 0x35 /* 5f */
-#define PAL_DAC_CONTROL 0x02 /* 61 */
-#define PAL_BURST_AMP 0x2f /* 62 */
-#define PAL_SUBC3 0xcb /* 63 */
-#define PAL_SUBC2 0x8a /* 64 */
-#define PAL_SUBC1 0x09 /* 65 */
-#define PAL_SUBC0 0x2a /* 66 */
-#define PAL_HTRIG 0x86 /* 6c */
-#define PAL_VTRIG 0x04 /* 6d */
-#define PAL_MULTI 0x20 /* 6e */
-#define PAL_CCTTX 0x15 /* 6f */
-#define PAL_FIRST_ACTIVE 0x16 /* 7a */
-#define PAL_LAST_ACTIVE 0x36 /* 7b */
-#define PAL_MSB_VERTICAL 0x40 /* 7c */
-
-/* Initialization Sequence */
-
-static __u8 init7121ntsc[] = {
- 0x26, 0x0, 0x27, 0x0,
- 0x28, NTSC_BURST_START, 0x29, NTSC_BURST_END,
- 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
- 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
- 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
- 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
- 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
- 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
- 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
- 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
- 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
- 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
- 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
- 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
- 0x5a, NTSC_CHROMA_PHASE, 0x5b, NTSC_GAINU,
- 0x5c, NTSC_GAINV, 0x5d, NTSC_BLACK_LEVEL,
- 0x5e, NTSC_BLANKING_LEVEL, 0x5f, NTSC_VBI_BLANKING,
- 0x60, 0x0, 0x61, NTSC_DAC_CONTROL,
- 0x62, NTSC_BURST_AMP, 0x63, NTSC_SUBC3,
- 0x64, NTSC_SUBC2, 0x65, NTSC_SUBC1,
- 0x66, NTSC_SUBC0, 0x67, 0x80, 0x68, 0x80,
- 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
- 0x6c, NTSC_HTRIG, 0x6d, NTSC_VTRIG,
- 0x6e, NTSC_MULTI, 0x6f, NTSC_CCTTX,
- 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
- 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
- 0x78, 0x0, 0x79, 0x0, 0x7a, NTSC_FIRST_ACTIVE,
- 0x7b, NTSC_LAST_ACTIVE, 0x7c, NTSC_MSB_VERTICAL,
- 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
-};
-#define INIT7121LEN (sizeof(init7121ntsc)/2)
-
-static __u8 init7121pal[] = {
- 0x26, 0x0, 0x27, 0x0,
- 0x28, PAL_BURST_START, 0x29, PAL_BURST_END,
- 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
- 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
- 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
- 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
- 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
- 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
- 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
- 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
- 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
- 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
- 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
- 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
- 0x5a, PAL_CHROMA_PHASE, 0x5b, PAL_GAINU,
- 0x5c, PAL_GAINV, 0x5d, PAL_BLACK_LEVEL,
- 0x5e, PAL_BLANKING_LEVEL, 0x5f, PAL_VBI_BLANKING,
- 0x60, 0x0, 0x61, PAL_DAC_CONTROL,
- 0x62, PAL_BURST_AMP, 0x63, PAL_SUBC3,
- 0x64, PAL_SUBC2, 0x65, PAL_SUBC1,
- 0x66, PAL_SUBC0, 0x67, 0x80, 0x68, 0x80,
- 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
- 0x6c, PAL_HTRIG, 0x6d, PAL_VTRIG,
- 0x6e, PAL_MULTI, 0x6f, PAL_CCTTX,
- 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
- 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
- 0x78, 0x0, 0x79, 0x0, 0x7a, PAL_FIRST_ACTIVE,
- 0x7b, PAL_LAST_ACTIVE, 0x7c, PAL_MSB_VERTICAL,
- 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
-};
-#endif
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 5dfd826d734e..cc7f3d6ee966 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1282,7 +1282,7 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_PHILIPS_EUROPA:
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
@@ -1322,7 +1322,7 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_KWORLD_DVBT_210:
if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
fe0->dvb.frontend = dvb_attach(tda10048_attach,
@@ -1340,17 +1340,17 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_PHILIPS_TIGER:
if (configure_tda827x_fe(dev, &philips_tiger_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_PINNACLE_PCTV_310i:
if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
&tda827x_cfg_1) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
&tda827x_cfg_1) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1150:
fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
@@ -1368,30 +1368,30 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_FLYDVBT_LR301:
if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_FLYDVB_TRIO:
if (!use_frontend) { /* terrestrial */
if (configure_tda827x_fe(dev, &lifeview_trio_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
} else { /* satellite */
fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
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__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
0x08, 0, 0) == NULL) {
wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
}
@@ -1407,7 +1407,7 @@ static int dvb_init(struct saa7134_dev *dev)
&ads_duo_cfg) == NULL) {
wprintk("no tda827x tuner found at addr: %02x\n",
ads_tech_duo_config.tuner_address);
- goto dettach_frontend;
+ goto detach_frontend;
}
} else
wprintk("failed to attach tda10046\n");
@@ -1415,13 +1415,13 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_TEVION_DVBT_220RF:
if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_MEDION_MD8800_QUADRO:
if (!use_frontend) { /* terrestrial */
if (configure_tda827x_fe(dev, &md8800_dvbt_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
} else { /* satellite */
fe0->dvb.frontend = dvb_attach(tda10086_attach,
&flydvbs, &dev->i2c_adap);
@@ -1435,7 +1435,7 @@ static int dvb_init(struct saa7134_dev *dev)
0x60, &dev->i2c_adap, 0) == NULL) {
wprintk("%s: Medion Quadro, no tda826x "
"found !\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dev_id != 0x08) {
/* we need to open the i2c gate (we know it exists) */
@@ -1444,7 +1444,7 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0x08, 0, 0) == NULL) {
wprintk("%s: Medion Quadro, no ISL6405 "
"found !\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dev_id == 0x07) {
/* fire up the 2nd section of the LNB supply since
@@ -1503,12 +1503,12 @@ static int dvb_init(struct saa7134_dev *dev)
if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
&dev->i2c_adap, 0) == NULL) {
wprintk("%s: No tda826x found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
&dev->i2c_adap, 0x08, 0, 0) == NULL) {
wprintk("%s: No ISL6421 found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
break;
@@ -1537,37 +1537,37 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_CINERGY_HT_PCMCIA:
if (configure_tda827x_fe(dev, &cinergy_ht_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_CINERGY_HT_PCI:
if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_PHILIPS_TIGER_S:
if (configure_tda827x_fe(dev, &philips_tiger_s_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_ASUS_P7131_4871:
if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_AVERMEDIA_SUPER_007:
if (configure_tda827x_fe(dev, &avermedia_super_007_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
&tda827x_cfg_2_sw42) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_PHILIPS_SNAKE:
fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
@@ -1576,24 +1576,24 @@ static int dvb_init(struct saa7134_dev *dev)
if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
&dev->i2c_adap, 0) == NULL) {
wprintk("%s: No tda826x found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
&dev->i2c_adap, 0, 0) == NULL) {
wprintk("%s: No lnbp21 found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
break;
case SAA7134_BOARD_CREATIX_CTX953:
if (configure_tda827x_fe(dev, &md8800_dvbt_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
if (configure_tda827x_fe(dev, &philips_tiger_s_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
dprintk("AverMedia E506R dvb setup\n");
@@ -1614,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
wprintk("%s: MD7134 DVB-S, no SD1878 "
"found !\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
/* we need to open the i2c gate (we know it exists) */
fe = fe0->dvb.frontend;
@@ -1623,7 +1623,7 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0x08, 0, 0) == NULL) {
wprintk("%s: MD7134 DVB-S, no ISL6405 "
"found !\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
fe->ops.i2c_gate_ctrl(fe, 0);
dev->original_set_voltage = fe->ops.set_voltage;
@@ -1645,7 +1645,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (!use_frontend) { /* terrestrial */
if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
} else { /* satellite */
fe0->dvb.frontend = dvb_attach(tda10086_attach,
&flydvbs, &dev->i2c_adap);
@@ -1655,13 +1655,13 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0) == NULL) {
wprintk("%s: Asus Tiger 3in1, no "
"tda826x found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
&dev->i2c_adap, 0, 0) == NULL) {
wprintk("%s: Asus Tiger 3in1, no lnbp21"
" found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
}
@@ -1670,7 +1670,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (!use_frontend) { /* terrestrial */
if (configure_tda827x_fe(dev, &asus_ps3_100_config,
&tda827x_cfg_2) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
} else { /* satellite */
fe0->dvb.frontend = dvb_attach(tda10086_attach,
&flydvbs, &dev->i2c_adap);
@@ -1680,13 +1680,13 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0) == NULL) {
wprintk("%s: Asus My Cinema PS3-100, no "
"tda826x found!\n", __func__);
- goto dettach_frontend;
+ 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"
" found!\n", __func__);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
}
@@ -1694,7 +1694,7 @@ static int dvb_init(struct saa7134_dev *dev)
case SAA7134_BOARD_ASUSTeK_TIGER:
if (configure_tda827x_fe(dev, &philips_tiger_config,
&tda827x_cfg_0) < 0)
- goto dettach_frontend;
+ goto detach_frontend;
break;
case SAA7134_BOARD_BEHOLD_H6:
fe0->dvb.frontend = dvb_attach(zl10353_attach,
@@ -1830,19 +1830,19 @@ static int dvb_init(struct saa7134_dev *dev)
};
if (!fe0->dvb.frontend)
- goto dettach_frontend;
+ goto detach_frontend;
fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
if (!fe) {
printk(KERN_ERR "%s/2: xc3028 attach failed\n",
dev->name);
- goto dettach_frontend;
+ goto detach_frontend;
}
}
if (NULL == fe0->dvb.frontend) {
printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
- goto dettach_frontend;
+ goto detach_frontend;
}
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = saa7134_tuner_callback;
@@ -1864,7 +1864,7 @@ static int dvb_init(struct saa7134_dev *dev)
}
return ret;
-dettach_frontend:
+detach_frontend:
videobuf_dvb_dealloc_frontends(&dev->frontends);
return -EINVAL;
}
diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h
deleted file mode 100644
index 9fadb331a40b..000000000000
--- a/drivers/media/video/saa7146.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- saa7146.h - definitions philips saa7146 based cards
- Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __SAA7146__
-#define __SAA7146__
-
-#define SAA7146_VERSION_CODE 0x000101
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-#ifndef O_NONCAP
-#define O_NONCAP O_TRUNC
-#endif
-
-#define MAX_GBUFFERS 2
-#define FBUF_SIZE 0x190000
-
-#ifdef __KERNEL__
-
-struct saa7146_window
-{
- int x, y;
- ushort width, height;
- ushort bpp, bpl;
- ushort swidth, sheight;
- short cropx, cropy;
- ushort cropwidth, cropheight;
- unsigned long vidadr;
- int color_fmt;
- ushort depth;
-};
-
-/* Per-open data for handling multiple opens on one device */
-struct device_open
-{
- int isopen;
- int noncapturing;
- struct saa7146 *dev;
-};
-#define MAX_OPENS 3
-
-struct saa7146
-{
- struct video_device video_dev;
- struct video_picture picture;
- struct video_audio audio_dev;
- struct video_info vidinfo;
- int user;
- int cap;
- int capuser;
- int irqstate; /* irq routine is state driven */
- int writemode;
- int playmode;
- unsigned int nr;
- unsigned long irq; /* IRQ used by SAA7146 card */
- unsigned short id;
- unsigned char revision;
- unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */
- unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */
- struct saa7146_window win;
- unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */
- struct device_open open_data[MAX_OPENS];
-#define MAX_MARKS 16
- /* for a/v sync */
- int endmark[MAX_MARKS], endmarkhead, endmarktail;
- u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2,
- *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out,
- *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in,
- *pagea1out, *pagea2in, *pagea2out;
- wait_queue_head_t i2cq, debiq, audq, vidq;
- u8 *vidbuf, *audbuf, *osdbuf, *dmadebi;
- int audhead, vidhead, osdhead, audtail, vidtail, osdtail;
- spinlock_t lock; /* the device lock */
-};
-#endif
-
-#ifdef _ALPHA_SAA7146
-#define saawrite(dat,adr) writel((dat), saa->saa7146_adr+(adr))
-#define saaread(adr) readl(saa->saa7146_adr+(adr))
-#else
-#define saawrite(dat,adr) writel((dat), saa->saa7146_mem+(adr))
-#define saaread(adr) readl(saa->saa7146_mem+(adr))
-#endif
-
-#define saaand(dat,adr) saawrite((dat) & saaread(adr), adr)
-#define saaor(dat,adr) saawrite((dat) | saaread(adr), adr)
-#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr)
-
-/* bitmask of attached hardware found */
-#define SAA7146_UNKNOWN 0x00000000
-#define SAA7146_SAA7111 0x00000001
-#define SAA7146_SAA7121 0x00000002
-#define SAA7146_IBMMPEG 0x00000004
-
-#endif
diff --git a/drivers/media/video/saa7146reg.h b/drivers/media/video/saa7146reg.h
deleted file mode 100644
index 80ec2c146b4c..000000000000
--- a/drivers/media/video/saa7146reg.h
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- saa7146.h - definitions philips saa7146 based cards
- Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __SAA7146_REG__
-#define __SAA7146_REG__
-#define SAA7146_BASE_ODD1 0x00
-#define SAA7146_BASE_EVEN1 0x04
-#define SAA7146_PROT_ADDR1 0x08
-#define SAA7146_PITCH1 0x0c
-#define SAA7146_PAGE1 0x10
-#define SAA7146_NUM_LINE_BYTE1 0x14
-#define SAA7146_BASE_ODD2 0x18
-#define SAA7146_BASE_EVEN2 0x1c
-#define SAA7146_PROT_ADDR2 0x20
-#define SAA7146_PITCH2 0x24
-#define SAA7146_PAGE2 0x28
-#define SAA7146_NUM_LINE_BYTE2 0x2c
-#define SAA7146_BASE_ODD3 0x30
-#define SAA7146_BASE_EVEN3 0x34
-#define SAA7146_PROT_ADDR3 0x38
-#define SAA7146_PITCH3 0x3c
-#define SAA7146_PAGE3 0x40
-#define SAA7146_NUM_LINE_BYTE3 0x44
-#define SAA7146_PCI_BT_V1 0x48
-#define SAA7146_PCI_BT_V2 0x49
-#define SAA7146_PCI_BT_V3 0x4a
-#define SAA7146_PCI_BT_DEBI 0x4b
-#define SAA7146_PCI_BT_A 0x4c
-#define SAA7146_DD1_INIT 0x50
-#define SAA7146_DD1_STREAM_B 0x54
-#define SAA7146_DD1_STREAM_A 0x56
-#define SAA7146_BRS_CTRL 0x58
-#define SAA7146_HPS_CTRL 0x5c
-#define SAA7146_HPS_V_SCALE 0x60
-#define SAA7146_HPS_V_GAIN 0x64
-#define SAA7146_HPS_H_PRESCALE 0x68
-#define SAA7146_HPS_H_SCALE 0x6c
-#define SAA7146_BCS_CTRL 0x70
-#define SAA7146_CHROMA_KEY_RANGE 0x74
-#define SAA7146_CLIP_FORMAT_CTRL 0x78
-#define SAA7146_DEBI_CONFIG 0x7c
-#define SAA7146_DEBI_COMMAND 0x80
-#define SAA7146_DEBI_PAGE 0x84
-#define SAA7146_DEBI_AD 0x88
-#define SAA7146_I2C_TRANSFER 0x8c
-#define SAA7146_I2C_STATUS 0x90
-#define SAA7146_BASE_A1_IN 0x94
-#define SAA7146_PROT_A1_IN 0x98
-#define SAA7146_PAGE_A1_IN 0x9C
-#define SAA7146_BASE_A1_OUT 0xa0
-#define SAA7146_PROT_A1_OUT 0xa4
-#define SAA7146_PAGE_A1_OUT 0xa8
-#define SAA7146_BASE_A2_IN 0xac
-#define SAA7146_PROT_A2_IN 0xb0
-#define SAA7146_PAGE_A2_IN 0xb4
-#define SAA7146_BASE_A2_OUT 0xb8
-#define SAA7146_PROT_A2_OUT 0xbc
-#define SAA7146_PAGE_A2_OUT 0xc0
-#define SAA7146_RPS_PAGE0 0xc4
-#define SAA7146_RPS_PAGE1 0xc8
-#define SAA7146_RPS_THRESH0 0xcc
-#define SAA7146_RPS_THRESH1 0xd0
-#define SAA7146_RPS_TOV0 0xd4
-#define SAA7146_RPS_TOV1 0xd8
-#define SAA7146_IER 0xdc
-#define SAA7146_GPIO_CTRL 0xe0
-#define SAA7146_EC1SSR 0xe4
-#define SAA7146_EC2SSR 0xe8
-#define SAA7146_ECT1R 0xec
-#define SAA7146_ECT2R 0xf0
-#define SAA7146_ACON1 0xf4
-#define SAA7146_ACON2 0xf8
-#define SAA7146_MC1 0xfc
-#define SAA7146_MC2 0x100
-#define SAA7146_RPS_ADDR0 0x104
-#define SAA7146_RPS_ADDR1 0x108
-#define SAA7146_ISR 0x10c
-#define SAA7146_PSR 0x110
-#define SAA7146_SSR 0x114
-#define SAA7146_EC1R 0x118
-#define SAA7146_EC2R 0x11c
-#define SAA7146_VDP1 0x120
-#define SAA7146_VDP2 0x124
-#define SAA7146_VDP3 0x128
-#define SAA7146_ADP1 0x12c
-#define SAA7146_ADP2 0x130
-#define SAA7146_ADP3 0x134
-#define SAA7146_ADP4 0x138
-#define SAA7146_DDP 0x13c
-#define SAA7146_LEVEL_REP 0x140
-#define SAA7146_FB_BUFFER1 0x144
-#define SAA7146_FB_BUFFER2 0x148
-#define SAA7146_A_TIME_SLOT1 0x180
-#define SAA7146_A_TIME_SLOT2 0x1C0
-
-/* bitfield defines */
-#define MASK_31 0x80000000
-#define MASK_30 0x40000000
-#define MASK_29 0x20000000
-#define MASK_28 0x10000000
-#define MASK_27 0x08000000
-#define MASK_26 0x04000000
-#define MASK_25 0x02000000
-#define MASK_24 0x01000000
-#define MASK_23 0x00800000
-#define MASK_22 0x00400000
-#define MASK_21 0x00200000
-#define MASK_20 0x00100000
-#define MASK_19 0x00080000
-#define MASK_18 0x00040000
-#define MASK_17 0x00020000
-#define MASK_16 0x00010000
-#define MASK_15 0x00008000
-#define MASK_14 0x00004000
-#define MASK_13 0x00002000
-#define MASK_12 0x00001000
-#define MASK_11 0x00000800
-#define MASK_10 0x00000400
-#define MASK_09 0x00000200
-#define MASK_08 0x00000100
-#define MASK_07 0x00000080
-#define MASK_06 0x00000040
-#define MASK_05 0x00000020
-#define MASK_04 0x00000010
-#define MASK_03 0x00000008
-#define MASK_02 0x00000004
-#define MASK_01 0x00000002
-#define MASK_00 0x00000001
-#define MASK_B0 0x000000ff
-#define MASK_B1 0x0000ff00
-#define MASK_B2 0x00ff0000
-#define MASK_B3 0xff000000
-#define MASK_W0 0x0000ffff
-#define MASK_W1 0xffff0000
-#define MASK_PA 0xfffffffc
-#define MASK_PR 0xfffffffe
-#define MASK_ER 0xffffffff
-#define MASK_NONE 0x00000000
-
-#define SAA7146_PAGE_MAP_EN MASK_11
-/* main control register 1 */
-#define SAA7146_MC1_MRST_N MASK_15
-#define SAA7146_MC1_ERPS1 MASK_13
-#define SAA7146_MC1_ERPS0 MASK_12
-#define SAA7146_MC1_EDP MASK_11
-#define SAA7146_MC1_EVP MASK_10
-#define SAA7146_MC1_EAP MASK_09
-#define SAA7146_MC1_EI2C MASK_08
-#define SAA7146_MC1_TR_E_DEBI MASK_07
-#define SAA7146_MC1_TR_E_1 MASK_06
-#define SAA7146_MC1_TR_E_2 MASK_05
-#define SAA7146_MC1_TR_E_3 MASK_04
-#define SAA7146_MC1_TR_E_A2_OUT MASK_03
-#define SAA7146_MC1_TR_E_A2_IN MASK_02
-#define SAA7146_MC1_TR_E_A1_OUT MASK_01
-#define SAA7146_MC1_TR_E_A1_IN MASK_00
-/* main control register 2 */
-#define SAA7146_MC2_RPS_SIG4 MASK_15
-#define SAA7146_MC2_RPS_SIG3 MASK_14
-#define SAA7146_MC2_RPS_SIG2 MASK_13
-#define SAA7146_MC2_RPS_SIG1 MASK_12
-#define SAA7146_MC2_RPS_SIG0 MASK_11
-#define SAA7146_MC2_UPLD_D1_B MASK_10
-#define SAA7146_MC2_UPLD_D1_A MASK_09
-#define SAA7146_MC2_UPLD_BRS MASK_08
-#define SAA7146_MC2_UPLD_HPS_H MASK_06
-#define SAA7146_MC2_UPLD_HPS_V MASK_05
-#define SAA7146_MC2_UPLD_DMA3 MASK_04
-#define SAA7146_MC2_UPLD_DMA2 MASK_03
-#define SAA7146_MC2_UPLD_DMA1 MASK_02
-#define SAA7146_MC2_UPLD_DEBI MASK_01
-#define SAA7146_MC2_UPLD_I2C MASK_00
-/* Primary Status Register and Interrupt Enable/Status Registers */
-#define SAA7146_PSR_PPEF MASK_31
-#define SAA7146_PSR_PABO MASK_30
-#define SAA7146_PSR_PPED MASK_29
-#define SAA7146_PSR_RPS_I1 MASK_28
-#define SAA7146_PSR_RPS_I0 MASK_27
-#define SAA7146_PSR_RPS_LATE1 MASK_26
-#define SAA7146_PSR_RPS_LATE0 MASK_25
-#define SAA7146_PSR_RPS_E1 MASK_24
-#define SAA7146_PSR_RPS_E0 MASK_23
-#define SAA7146_PSR_RPS_TO1 MASK_22
-#define SAA7146_PSR_RPS_TO0 MASK_21
-#define SAA7146_PSR_UPLD MASK_20
-#define SAA7146_PSR_DEBI_S MASK_19
-#define SAA7146_PSR_DEBI_E MASK_18
-#define SAA7146_PSR_I2C_S MASK_17
-#define SAA7146_PSR_I2C_E MASK_16
-#define SAA7146_PSR_A2_IN MASK_15
-#define SAA7146_PSR_A2_OUT MASK_14
-#define SAA7146_PSR_A1_IN MASK_13
-#define SAA7146_PSR_A1_OUT MASK_12
-#define SAA7146_PSR_AFOU MASK_11
-#define SAA7146_PSR_V_PE MASK_10
-#define SAA7146_PSR_VFOU MASK_09
-#define SAA7146_PSR_FIDA MASK_08
-#define SAA7146_PSR_FIDB MASK_07
-#define SAA7146_PSR_PIN3 MASK_06
-#define SAA7146_PSR_PIN2 MASK_05
-#define SAA7146_PSR_PIN1 MASK_04
-#define SAA7146_PSR_PIN0 MASK_03
-#define SAA7146_PSR_ECS MASK_02
-#define SAA7146_PSR_EC3S MASK_01
-#define SAA7146_PSR_EC0S MASK_00
-/* Secondary Status Register */
-#define SAA7146_SSR_PRQ MASK_31
-#define SAA7146_SSR_PMA MASK_30
-#define SAA7146_SSR_RPS_RE1 MASK_29
-#define SAA7146_SSR_RPS_PE1 MASK_28
-#define SAA7146_SSR_RPS_A1 MASK_27
-#define SAA7146_SSR_RPS_RE0 MASK_26
-#define SAA7146_SSR_RPS_PE0 MASK_25
-#define SAA7146_SSR_RPS_A0 MASK_24
-#define SAA7146_SSR_DEBI_TO MASK_23
-#define SAA7146_SSR_DEBI_EF MASK_22
-#define SAA7146_SSR_I2C_EA MASK_21
-#define SAA7146_SSR_I2C_EW MASK_20
-#define SAA7146_SSR_I2C_ER MASK_19
-#define SAA7146_SSR_I2C_EL MASK_18
-#define SAA7146_SSR_I2C_EF MASK_17
-#define SAA7146_SSR_V3P MASK_16
-#define SAA7146_SSR_V2P MASK_15
-#define SAA7146_SSR_V1P MASK_14
-#define SAA7146_SSR_VF3 MASK_13
-#define SAA7146_SSR_VF2 MASK_12
-#define SAA7146_SSR_VF1 MASK_11
-#define SAA7146_SSR_AF2_IN MASK_10
-#define SAA7146_SSR_AF2_OUT MASK_09
-#define SAA7146_SSR_AF1_IN MASK_08
-#define SAA7146_SSR_AF1_OUT MASK_07
-#define SAA7146_SSR_VGT MASK_05
-#define SAA7146_SSR_LNQG MASK_04
-#define SAA7146_SSR_EC5S MASK_03
-#define SAA7146_SSR_EC4S MASK_02
-#define SAA7146_SSR_EC2S MASK_01
-#define SAA7146_SSR_EC1S MASK_00
-/* I2C status register */
-#define SAA7146_I2C_ABORT MASK_07
-#define SAA7146_I2C_SPERR MASK_06
-#define SAA7146_I2C_APERR MASK_05
-#define SAA7146_I2C_DTERR MASK_04
-#define SAA7146_I2C_DRERR MASK_03
-#define SAA7146_I2C_AL MASK_02
-#define SAA7146_I2C_ERR MASK_01
-#define SAA7146_I2C_BUSY MASK_00
-/* output formats */
-#define SAA7146_YUV422 0
-#define SAA7146_RGB16 0
-#define SAA7146_YUV444 1
-#define SAA7146_RGB24 1
-#define SAA7146_ARGB32 2
-#define SAA7146_YUV411 3
-#define SAA7146_ARGB15 3
-#define SAA7146_YUV2 4
-#define SAA7146_RGAB15 4
-#define SAA7146_Y8 6
-#define SAA7146_YUV8 7
-#define SAA7146_RGB8 7
-#define SAA7146_YUV444p 8
-#define SAA7146_YUV422p 9
-#define SAA7146_YUV420p 10
-#define SAA7146_YUV1620 11
-#define SAA7146_Y1 13
-#define SAA7146_Y2 14
-#define SAA7146_YUV1 15
-#endif
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
index 8a98ab68239e..c8799fdaae67 100644
--- a/drivers/media/video/saa7164/saa7164-api.c
+++ b/drivers/media/video/saa7164/saa7164-api.c
@@ -1367,7 +1367,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
struct saa7164_dev *dev = bus->dev;
u16 len = 0;
int unitid;
- u32 regval;
u8 buf[256];
int ret;
@@ -1376,19 +1375,6 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
if (reglen > 4)
return -EIO;
- if (reglen == 1)
- regval = *(reg);
- else
- if (reglen == 2)
- regval = ((*(reg) << 8) || *(reg+1));
- else
- if (reglen == 3)
- regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2));
- else
- if (reglen == 4)
- regval = ((*(reg) << 24) | (*(reg+1) << 16) |
- (*(reg+2) << 8) | *(reg+3));
-
/* Prepare the send buffer */
/* Bytes 00-03 source register length
* 04-07 source bytes to read
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c
index 26148f76cba2..4f7e3b42263f 100644
--- a/drivers/media/video/saa7164/saa7164-i2c.c
+++ b/drivers/media/video/saa7164/saa7164-i2c.c
@@ -69,15 +69,6 @@ err:
return retval;
}
-void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd,
- void *arg)
-{
- if (bus->i2c_rc != 0)
- return;
-
- i2c_clients_command(&bus->i2c_adap, cmd, arg);
-}
-
static u32 saa7164_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C;
@@ -106,21 +97,14 @@ int saa7164_i2c_register(struct saa7164_i2c *bus)
dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr);
- memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template,
- sizeof(bus->i2c_adap));
-
- memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template,
- sizeof(bus->i2c_algo));
-
- memcpy(&bus->i2c_client, &saa7164_i2c_client_template,
- sizeof(bus->i2c_client));
+ bus->i2c_adap = saa7164_i2c_adap_template;
+ bus->i2c_client = saa7164_i2c_client_template;
bus->i2c_adap.dev.parent = &dev->pci->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name,
sizeof(bus->i2c_adap.name));
- bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, bus);
i2c_add_adapter(&bus->i2c_adap);
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 8d120e3baf70..35219b9b0fbc 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -46,7 +46,6 @@
#include <linux/pci.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/kdev_t.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
@@ -251,7 +250,6 @@ struct saa7164_i2c {
/* I2C I/O */
struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
};
diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig
index fb99ff18be07..3149cda1d0db 100644
--- a/drivers/media/video/smiapp/Kconfig
+++ b/drivers/media/video/smiapp/Kconfig
@@ -1,6 +1,7 @@
config VIDEO_SMIAPP
tristate "SMIA++/SMIA sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK
+ depends on MEDIA_CAMERA_SUPPORT
select VIDEO_SMIAPP_PLL
---help---
This is a generic driver for SMIA++/SMIA camera modules.
diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c
index 9cf5bda35fbe..bfd47c106134 100644
--- a/drivers/media/video/smiapp/smiapp-core.c
+++ b/drivers/media/video/smiapp/smiapp-core.c
@@ -33,15 +33,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
#include <linux/v4l2-mediabus.h>
#include <media/v4l2-device.h>
#include "smiapp.h"
-#define SMIAPP_ALIGN_DIM(dim, flags) \
- ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \
- ? ALIGN((dim), 2) \
+#define SMIAPP_ALIGN_DIM(dim, flags) \
+ ((flags) & V4L2_SEL_FLAG_GE \
+ ? ALIGN((dim), 2) \
: (dim) & ~1)
/*
@@ -1631,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev,
smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
switch (target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
comp->width = crops[SMIAPP_PAD_SINK]->width;
comp->height = crops[SMIAPP_PAD_SINK]->height;
if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
@@ -1647,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev,
}
}
/* Fall through */
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SEL_TGT_COMPOSE:
*crops[SMIAPP_PAD_SRC] = *comp;
break;
default:
@@ -1723,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev,
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
ssd->sink_fmt = *crops[ssd->sink_pad];
smiapp_propagate(subdev, fh, fmt->which,
- V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+ V4L2_SEL_TGT_CROP);
mutex_unlock(&sensor->mutex);
@@ -1748,14 +1747,14 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
h &= ~1;
ask_h &= ~1;
- if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) {
+ if (flags & V4L2_SEL_FLAG_GE) {
if (w < ask_w)
val -= SCALING_GOODNESS;
if (h < ask_h)
val -= SCALING_GOODNESS;
}
- if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) {
+ if (flags & V4L2_SEL_FLAG_LE) {
if (w > ask_w)
val -= SCALING_GOODNESS;
if (h > ask_h)
@@ -1958,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev,
*comp = sel->r;
smiapp_propagate(subdev, fh, sel->which,
- V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL);
+ V4L2_SEL_TGT_COMPOSE);
if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
return smiapp_update_mode(sensor);
@@ -1974,8 +1973,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
/* We only implement crop in three places. */
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
if (ssd == sensor->pixel_array
&& sel->pad == SMIAPP_PA_PAD_SRC)
return 0;
@@ -1988,8 +1987,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
== SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
return 0;
return -EINVAL;
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
if (sel->pad == ssd->source_pad)
return -EINVAL;
if (ssd == sensor->binner)
@@ -2051,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev,
if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
smiapp_propagate(subdev, fh, sel->which,
- V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+ V4L2_SEL_TGT_CROP);
return 0;
}
@@ -2085,7 +2084,7 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev,
}
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
if (ssd == sensor->pixel_array) {
sel->r.width =
sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
@@ -2097,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev,
sel->r = *comp;
}
break;
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
sel->r = *crops[sel->pad];
break;
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SEL_TGT_COMPOSE:
sel->r = *comp;
break;
}
@@ -2148,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev,
sel->r.height);
switch (sel->target) {
- case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SEL_TGT_CROP:
ret = smiapp_set_crop(subdev, fh, sel);
break;
- case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SEL_TGT_COMPOSE:
ret = smiapp_set_compose(subdev, fh, sel);
break;
default:
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
index 22ea211ab54f..2bc153e869be 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -182,7 +182,7 @@ do { \
# define V4LDBG(level, name, cmd) \
do { \
if (debug >= (level)) \
- v4l_print_ioctl(name, cmd); \
+ v4l_printk_ioctl(name, cmd); \
} while (0)
# define KDBG(level, fmt, args...) \
do { \
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 0421bf9453b4..b03ffecb7438 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -62,7 +62,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd,
}
if (icl->power) {
- ret = icl->power(icd->pdev, 1);
+ ret = icl->power(icd->control, 1);
if (ret < 0) {
dev_err(icd->pdev,
"Platform failed to power-on the camera.\n");
@@ -78,7 +78,7 @@ static int soc_camera_power_on(struct soc_camera_device *icd,
esdpwr:
if (icl->power)
- icl->power(icd->pdev, 0);
+ icl->power(icd->control, 0);
elinkpwr:
regulator_bulk_disable(icl->num_regulators,
icl->regulators);
@@ -95,7 +95,7 @@ static int soc_camera_power_off(struct soc_camera_device *icd,
return ret;
if (icl->power) {
- ret = icl->power(icd->pdev, 0);
+ ret = icl->power(icd->control, 0);
if (ret < 0) {
dev_err(icd->pdev,
"Platform failed to power-off the camera.\n");
@@ -1518,6 +1518,7 @@ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
}
static struct platform_driver __refdata soc_camera_pdrv = {
+ .probe = soc_camera_pdrv_probe,
.remove = __devexit_p(soc_camera_pdrv_remove),
.driver = {
.name = "soc-camera-pdrv",
@@ -1527,7 +1528,7 @@ static struct platform_driver __refdata soc_camera_pdrv = {
static int __init soc_camera_init(void)
{
- return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
+ return platform_driver_register(&soc_camera_pdrv);
}
static void __exit soc_camera_exit(void)
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c
index c096b3f74200..7b1f6ebd0e2c 100644
--- a/drivers/media/video/tlg2300/pd-main.c
+++ b/drivers/media/video/tlg2300/pd-main.c
@@ -53,7 +53,8 @@ int debug_mode;
module_param(debug_mode, int, 0644);
MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
-static const char *firmware_name = "tlg2300_firmware.bin";
+#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
+static const char *firmware_name = TLG2300_FIRMWARE;
static struct usb_driver poseidon_driver;
static LIST_HEAD(pd_device_list);
@@ -532,3 +533,4 @@ MODULE_AUTHOR("Telegent Systems");
MODULE_DESCRIPTION("For tlg2300-based USB device ");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.2");
+MODULE_FIRMWARE(TLG2300_FIRMWARE);
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 1ad5ab6ce5cf..b5a819af2b8c 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -228,6 +228,16 @@ static int fe_has_signal(struct dvb_frontend *fe)
return strength;
}
+static int fe_get_afc(struct dvb_frontend *fe)
+{
+ s32 afc = 0;
+
+ if (fe->ops.tuner_ops.get_afc)
+ fe->ops.tuner_ops.get_afc(fe, &afc);
+
+ return 0;
+}
+
static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
@@ -247,6 +257,7 @@ static struct analog_demod_ops tuner_analog_ops = {
.set_params = fe_set_params,
.standby = fe_standby,
.has_signal = fe_has_signal,
+ .get_afc = fe_get_afc,
.set_config = fe_set_config,
.tuner_status = tuner_status
};
@@ -1178,6 +1189,8 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
if (vt->type == t->mode && analog_ops->get_afc)
vt->afc = analog_ops->get_afc(&t->fe);
+ if (analog_ops->has_signal)
+ vt->signal = analog_ops->has_signal(&t->fe);
if (vt->type != V4L2_TUNER_RADIO) {
vt->capability |= V4L2_TUNER_CAP_NORM;
vt->rangelow = tv_range[0] * 16;
@@ -1197,8 +1210,6 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
V4L2_TUNER_SUB_STEREO :
V4L2_TUNER_SUB_MONO;
}
- if (analog_ops->has_signal)
- vt->signal = analog_ops->has_signal(&t->fe);
vt->audmode = t->audmode;
}
vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index c5b1a7365e4f..321b3153df87 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -59,8 +59,8 @@ struct CHIPSTATE;
typedef int (*getvalue)(int);
typedef int (*checkit)(struct CHIPSTATE*);
typedef int (*initialize)(struct CHIPSTATE*);
-typedef int (*getmode)(struct CHIPSTATE*);
-typedef void (*setmode)(struct CHIPSTATE*, int mode);
+typedef int (*getrxsubchans)(struct CHIPSTATE *);
+typedef void (*setaudmode)(struct CHIPSTATE*, int mode);
/* i2c command */
typedef struct AUDIOCMD {
@@ -96,8 +96,8 @@ struct CHIPDESC {
getvalue volfunc,treblefunc,bassfunc;
/* get/set mode */
- getmode getmode;
- setmode setmode;
+ getrxsubchans getrxsubchans;
+ setaudmode setaudmode;
/* input switch register + values for v4l inputs */
int inputreg;
@@ -118,7 +118,7 @@ struct CHIPSTATE {
audiocmd shadow;
/* current settings */
- __u16 left,right,treble,bass,muted,mode;
+ __u16 left, right, treble, bass, muted;
int prevmode;
int radio;
int input;
@@ -126,7 +126,6 @@ struct CHIPSTATE {
/* thread */
struct task_struct *thread;
struct timer_list wt;
- int watch_stereo;
int audmode;
};
@@ -288,7 +287,7 @@ static int chip_thread(void *data)
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chip->desc;
struct v4l2_subdev *sd = &chip->sd;
- int mode;
+ int mode, selected;
v4l2_dbg(1, debug, sd, "thread started\n");
set_freezable();
@@ -302,12 +301,12 @@ static int chip_thread(void *data)
break;
v4l2_dbg(1, debug, sd, "thread wakeup\n");
- /* don't do anything for radio or if mode != auto */
- if (chip->radio || chip->mode != 0)
+ /* don't do anything for radio */
+ if (chip->radio)
continue;
/* have a look what's going on */
- mode = desc->getmode(chip);
+ mode = desc->getrxsubchans(chip);
if (mode == chip->prevmode)
continue;
@@ -316,16 +315,32 @@ static int chip_thread(void *data)
chip->prevmode = mode;
- if (mode & V4L2_TUNER_MODE_STEREO)
- desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
- if (mode & V4L2_TUNER_MODE_LANG1_LANG2)
- desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
- else if (mode & V4L2_TUNER_MODE_LANG1)
- desc->setmode(chip, V4L2_TUNER_MODE_LANG1);
- else if (mode & V4L2_TUNER_MODE_LANG2)
- desc->setmode(chip, V4L2_TUNER_MODE_LANG2);
- else
- desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+ selected = V4L2_TUNER_MODE_MONO;
+ switch (chip->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ if (mode & V4L2_TUNER_SUB_LANG1)
+ selected = V4L2_TUNER_MODE_LANG1;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ if (mode & V4L2_TUNER_SUB_LANG1)
+ selected = V4L2_TUNER_MODE_LANG1;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ if (mode & V4L2_TUNER_SUB_LANG2)
+ selected = V4L2_TUNER_MODE_LANG2;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ if (mode & V4L2_TUNER_SUB_LANG2)
+ selected = V4L2_TUNER_MODE_LANG1_LANG2;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ }
+ desc->setaudmode(chip, selected);
/* schedule next check */
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
@@ -358,24 +373,25 @@ static int chip_thread(void *data)
#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */
#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */
-static int tda9840_getmode(struct CHIPSTATE *chip)
+static int tda9840_getrxsubchans(struct CHIPSTATE *chip)
{
struct v4l2_subdev *sd = &chip->sd;
int val, mode;
val = chip_read(chip);
- mode = V4L2_TUNER_MODE_MONO;
+ mode = V4L2_TUNER_SUB_MONO;
if (val & TDA9840_DS_DUAL)
- mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
if (val & TDA9840_ST_STEREO)
- mode |= V4L2_TUNER_MODE_STEREO;
+ mode = V4L2_TUNER_SUB_STEREO;
- v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd,
+ "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
-static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode)
{
int update = 1;
int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
@@ -393,6 +409,9 @@ static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
case V4L2_TUNER_MODE_LANG2:
t |= TDA9840_DUALB;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ t |= TDA9840_DUALAB;
+ break;
default:
update = 0;
}
@@ -477,6 +496,7 @@ static int tda9840_checkit(struct CHIPSTATE *chip)
/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
/* Common to TDA9855 and TDA9850: */
#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
+#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */
#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
#define TDA985x_MONO 0 /* Forces Mono output */
#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
@@ -513,18 +533,22 @@ static int tda9855_volume(int val) { return val/0x2e8+0x27; }
static int tda9855_bass(int val) { return val/0xccc+0x06; }
static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
-static int tda985x_getmode(struct CHIPSTATE *chip)
+static int tda985x_getrxsubchans(struct CHIPSTATE *chip)
{
- int mode;
+ int mode, val;
- mode = ((TDA985x_STP | TDA985x_SAPP) &
- chip_read(chip)) >> 4;
/* Add mono mode regardless of SAP and stereo */
/* Allows forced mono */
- return mode | V4L2_TUNER_MODE_MONO;
+ mode = V4L2_TUNER_SUB_MONO;
+ val = chip_read(chip);
+ if (val & TDA985x_STP)
+ mode = V4L2_TUNER_SUB_STEREO;
+ if (val & TDA985x_SAPP)
+ mode |= V4L2_TUNER_SUB_SAP;
+ return mode;
}
-static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
+static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode)
{
int update = 1;
int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
@@ -534,11 +558,15 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
c6 |= TDA985x_MONO;
break;
case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
c6 |= TDA985x_STEREO;
break;
- case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_SAP:
c6 |= TDA985x_SAP;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ c6 |= TDA985x_MONOSAP;
+ break;
default:
update = 0;
}
@@ -583,9 +611,10 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
#define TDA9873_TR_MASK (7 << 2)
#define TDA9873_TR_MONO 4
#define TDA9873_TR_STEREO 1 << 4
-#define TDA9873_TR_REVERSE (1 << 3) & (1 << 2)
+#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2))
#define TDA9873_TR_DUALA 1 << 2
#define TDA9873_TR_DUALB 1 << 3
+#define TDA9873_TR_DUALAB 0
/* output level controls
* B5: output level switch (0 = reduced gain, 1 = normal gain)
@@ -653,46 +682,51 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
#define TDA9873_MOUT_DUALA 0
#define TDA9873_MOUT_DUALB 1 << 3
#define TDA9873_MOUT_ST 1 << 4
-#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3)
+#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3))
#define TDA9873_MOUT_EXTL 1 << 5
-#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3)
-#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4)
-#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3)
+#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3))
+#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4))
+#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3))
/* Status bits: (chip read) */
#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */
#define TDA9873_STEREO 2 /* Stereo sound is identified */
#define TDA9873_DUAL 4 /* Dual sound is identified */
-static int tda9873_getmode(struct CHIPSTATE *chip)
+static int tda9873_getrxsubchans(struct CHIPSTATE *chip)
{
struct v4l2_subdev *sd = &chip->sd;
int val,mode;
val = chip_read(chip);
- mode = V4L2_TUNER_MODE_MONO;
+ mode = V4L2_TUNER_SUB_MONO;
if (val & TDA9873_STEREO)
- mode |= V4L2_TUNER_MODE_STEREO;
+ mode = V4L2_TUNER_SUB_STEREO;
if (val & TDA9873_DUAL)
- mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ v4l2_dbg(1, debug, sd,
+ "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
-static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode)
{
struct v4l2_subdev *sd = &chip->sd;
int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
/* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): external input\n");
return;
}
- v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data);
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n",
+ TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n",
+ sw_data);
switch (mode) {
case V4L2_TUNER_MODE_MONO:
@@ -707,13 +741,16 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
case V4L2_TUNER_MODE_LANG2:
sw_data |= TDA9873_TR_DUALB;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ sw_data |= TDA9873_TR_DUALAB;
+ break;
default:
- chip->mode = 0;
return;
}
chip_write(chip, TDA9873_SW, sw_data);
- v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): req. mode %d; chip_write: %d\n",
mode, sw_data);
}
@@ -859,13 +896,13 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
return 1;
}
-static int tda9874a_getmode(struct CHIPSTATE *chip)
+static int tda9874a_getrxsubchans(struct CHIPSTATE *chip)
{
struct v4l2_subdev *sd = &chip->sd;
int dsr,nsr,mode;
int necr; /* just for debugging */
- mode = V4L2_TUNER_MODE_MONO;
+ mode = V4L2_TUNER_SUB_MONO;
if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
return mode;
@@ -888,22 +925,23 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
* external 4052 multiplexer in audio_hook().
*/
if(nsr & 0x02) /* NSR.S/MB=1 */
- mode |= V4L2_TUNER_MODE_STEREO;
+ mode = V4L2_TUNER_SUB_STEREO;
if(nsr & 0x01) /* NSR.D/SB=1 */
- mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
} else {
if(dsr & 0x02) /* DSR.IDSTE=1 */
- mode |= V4L2_TUNER_MODE_STEREO;
+ mode = V4L2_TUNER_SUB_STEREO;
if(dsr & 0x04) /* DSR.IDDUA=1 */
- mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
}
- v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
dsr, nsr, necr, mode);
return mode;
}
-static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
+static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode)
{
struct v4l2_subdev *sd = &chip->sd;
@@ -939,14 +977,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
aosr = 0xa0; /* auto-select, dual B/B */
mdacosr = (tda9874a_mode) ? 0x83:0x81;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ aosr = 0x00; /* always route L to L and R to R */
+ mdacosr = (tda9874a_mode) ? 0x82:0x80;
+ break;
default:
- chip->mode = 0;
return;
}
chip_write(chip, TDA9874A_AOSR, aosr);
chip_write(chip, TDA9874A_MDACOSR, mdacosr);
- v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
mode, aosr, mdacosr);
} else { /* dic == 0x07 */
@@ -974,14 +1016,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
fmmr = 0x02; /* dual */
aosr = 0x20; /* dual B/B */
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ fmmr = 0x02; /* dual */
+ aosr = 0x00; /* dual A/B */
+ break;
default:
- chip->mode = 0;
return;
}
chip_write(chip, TDA9874A_FMMR, fmmr);
chip_write(chip, TDA9874A_AOSR, aosr);
- v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
mode, fmmr, aosr);
}
}
@@ -1226,25 +1272,33 @@ static int tea6320_initialize(struct CHIPSTATE * chip)
static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
-static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
+static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode)
{
int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
- if (mode & V4L2_TUNER_MODE_LANG1) {
+ switch (mode) {
+ case V4L2_TUNER_MODE_LANG1:
s1 |= TDA8425_S1_ML_SOUND_A;
s1 |= TDA8425_S1_STEREO_PSEUDO;
-
- } else if (mode & V4L2_TUNER_MODE_LANG2) {
+ break;
+ case V4L2_TUNER_MODE_LANG2:
s1 |= TDA8425_S1_ML_SOUND_B;
s1 |= TDA8425_S1_STEREO_PSEUDO;
-
- } else {
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
s1 |= TDA8425_S1_ML_STEREO;
-
- if (mode & V4L2_TUNER_MODE_MONO)
- s1 |= TDA8425_S1_STEREO_MONO;
- if (mode & V4L2_TUNER_MODE_STEREO)
- s1 |= TDA8425_S1_STEREO_SPATIAL;
+ s1 |= TDA8425_S1_STEREO_LINEAR;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ s1 |= TDA8425_S1_ML_STEREO;
+ s1 |= TDA8425_S1_STEREO_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ s1 |= TDA8425_S1_ML_STEREO;
+ s1 |= TDA8425_S1_STEREO_SPATIAL;
+ break;
+ default:
+ return;
}
chip_write(chip,TDA8425_S1,s1);
}
@@ -1297,18 +1351,20 @@ static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
* stereo L L
* BIL H L
*/
-static int ta8874z_getmode(struct CHIPSTATE *chip)
+static int ta8874z_getrxsubchans(struct CHIPSTATE *chip)
{
int val, mode;
val = chip_read(chip);
- mode = V4L2_TUNER_MODE_MONO;
+ mode = V4L2_TUNER_SUB_MONO;
if (val & TA8874Z_B1){
- mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
}else if (!(val & TA8874Z_B0)){
- mode |= V4L2_TUNER_MODE_STEREO;
+ mode = V4L2_TUNER_SUB_STEREO;
}
- /* v4l_dbg(1, debug, chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */
+ /* v4l2_dbg(1, debug, &chip->sd,
+ "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n",
+ val, mode); */
return mode;
}
@@ -1316,14 +1372,15 @@ static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}};
static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}};
static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}};
static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
-static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
+static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode)
{
struct v4l2_subdev *sd = &chip->sd;
int update = 1;
audiocmd *t = NULL;
- v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+ v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode);
switch(mode){
case V4L2_TUNER_MODE_MONO:
@@ -1338,6 +1395,9 @@ static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
case V4L2_TUNER_MODE_LANG2:
t = &ta8874z_sub;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ t = &ta8874z_both;
+ break;
default:
update = 0;
}
@@ -1394,8 +1454,8 @@ static struct CHIPDESC chiplist[] = {
/* callbacks */
.checkit = tda9840_checkit,
- .getmode = tda9840_getmode,
- .setmode = tda9840_setmode,
+ .getrxsubchans = tda9840_getrxsubchans,
+ .setaudmode = tda9840_setaudmode,
.init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN
/* ,TDA9840_SW, TDA9840_MONO */} }
@@ -1410,8 +1470,8 @@ static struct CHIPDESC chiplist[] = {
/* callbacks */
.checkit = tda9873_checkit,
- .getmode = tda9873_getmode,
- .setmode = tda9873_setmode,
+ .getrxsubchans = tda9873_getrxsubchans,
+ .setaudmode = tda9873_setaudmode,
.init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } },
.inputreg = TDA9873_SW,
@@ -1430,8 +1490,8 @@ static struct CHIPDESC chiplist[] = {
/* callbacks */
.initialize = tda9874a_initialize,
.checkit = tda9874a_checkit,
- .getmode = tda9874a_getmode,
- .setmode = tda9874a_setmode,
+ .getrxsubchans = tda9874a_getrxsubchans,
+ .setaudmode = tda9874a_setaudmode,
},
{
.name = "tda9875",
@@ -1460,8 +1520,8 @@ static struct CHIPDESC chiplist[] = {
.addr_hi = I2C_ADDR_TDA985x_H >> 1,
.registers = 11,
- .getmode = tda985x_getmode,
- .setmode = tda985x_setmode,
+ .getrxsubchans = tda985x_getrxsubchans,
+ .setaudmode = tda985x_setaudmode,
.init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
},
@@ -1482,8 +1542,8 @@ static struct CHIPDESC chiplist[] = {
.volfunc = tda9855_volume,
.bassfunc = tda9855_bass,
.treblefunc = tda9855_treble,
- .getmode = tda985x_getmode,
- .setmode = tda985x_setmode,
+ .getrxsubchans = tda985x_getrxsubchans,
+ .setaudmode = tda985x_setaudmode,
.init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
@@ -1564,7 +1624,7 @@ static struct CHIPDESC chiplist[] = {
.volfunc = tda8425_shift10,
.bassfunc = tda8425_shift12,
.treblefunc = tda8425_shift12,
- .setmode = tda8425_setmode,
+ .setaudmode = tda8425_setaudmode,
.inputreg = TDA8425_S1,
.inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
@@ -1593,11 +1653,10 @@ static struct CHIPDESC chiplist[] = {
.addr_lo = I2C_ADDR_TDA9840 >> 1,
.addr_hi = I2C_ADDR_TDA9840 >> 1,
.registers = 2,
- .flags = CHIP_NEED_CHECKMODE,
/* callbacks */
- .getmode = ta8874z_getmode,
- .setmode = ta8874z_setmode,
+ .getrxsubchans = ta8874z_getrxsubchans,
+ .setaudmode = ta8874z_setaudmode,
.init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
},
@@ -1736,7 +1795,6 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd)
struct CHIPSTATE *chip = to_state(sd);
chip->radio = 1;
- chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
return 0;
}
@@ -1793,9 +1851,8 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
- int mode = 0;
- if (!desc->setmode)
+ if (!desc->setaudmode)
return 0;
if (chip->radio)
return 0;
@@ -1805,22 +1862,18 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1:
case V4L2_TUNER_MODE_LANG2:
- mode = vt->audmode;
- break;
case V4L2_TUNER_MODE_LANG1_LANG2:
- mode = V4L2_TUNER_MODE_STEREO;
break;
default:
return -EINVAL;
}
chip->audmode = vt->audmode;
- if (mode) {
- chip->watch_stereo = 0;
- /* del_timer(&chip->wt); */
- chip->mode = mode;
- desc->setmode(chip, mode);
- }
+ if (chip->thread)
+ wake_up_process(chip->thread);
+ else
+ desc->setaudmode(chip, vt->audmode);
+
return 0;
}
@@ -1828,30 +1881,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
- int mode = V4L2_TUNER_MODE_MONO;
- if (!desc->getmode)
+ if (!desc->getrxsubchans)
return 0;
if (chip->radio)
return 0;
vt->audmode = chip->audmode;
- vt->rxsubchans = 0;
+ vt->rxsubchans = desc->getrxsubchans(chip);
vt->capability = V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- mode = desc->getmode(chip);
-
- if (mode & V4L2_TUNER_MODE_MONO)
- vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
- if (mode & V4L2_TUNER_MODE_STEREO)
- vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
- /* Note: for SAP it should be mono/lang2 or stereo/lang2.
- When this module is converted fully to v4l2, then this
- should change for those chips that can detect SAP. */
- if (mode & V4L2_TUNER_MODE_LANG1)
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
- V4L2_TUNER_SUB_LANG2;
return 0;
}
@@ -1868,9 +1908,7 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
- chip->mode = 0; /* automatic */
-
- /* For chips that provide getmode and setmode, and doesn't
+ /* For chips that provide getrxsubchans and setaudmode, and doesn't
automatically follows the stereo carrier, a kthread is
created to set the audio standard. In this case, when then
the video channel is changed, tvaudio starts on MONO mode.
@@ -1879,9 +1917,8 @@ static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
audio carrier.
*/
if (chip->thread) {
- desc->setmode(chip, V4L2_TUNER_MODE_MONO);
- if (chip->prevmode != V4L2_TUNER_MODE_MONO)
- chip->prevmode = -1; /* reset previous mode */
+ desc->setaudmode(chip, V4L2_TUNER_MODE_MONO);
+ chip->prevmode = -1; /* reset previous mode */
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
return 0;
@@ -2023,7 +2060,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *
chip->thread = NULL;
init_timer(&chip->wt);
if (desc->flags & CHIP_NEED_CHECKMODE) {
- if (!desc->getmode || !desc->setmode) {
+ if (!desc->getrxsubchans || !desc->setaudmode) {
/* This shouldn't be happen. Warn user, but keep working
without kthread
*/
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index b7867427e5c4..a751b6c146fd 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -61,13 +61,20 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
int rc;
buffer[0] = addr;
- if (1 != (rc = i2c_master_send(c, buffer, 1)))
- v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+ rc = i2c_master_send(c, buffer, 1);
+ if (rc < 0) {
+ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
msleep(10);
- if (1 != (rc = i2c_master_recv(c, buffer, 1)))
- v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ rc = i2c_master_recv(c, buffer, 1);
+ if (rc < 0) {
+ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
@@ -250,7 +257,7 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
int opmode = 0;
struct tvp5150 *decoder = to_tvp5150(sd);
int input = 0;
- unsigned char val;
+ int val;
if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
input = 8;
@@ -279,6 +286,11 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd)
* For Composite and TV, it should be the reverse
*/
val = tvp5150_read(sd, TVP5150_MISC_CTL);
+ if (val < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n", __func__, val);
+ return;
+ }
+
if (decoder->input == TVP5150_SVIDEO)
val = (val & ~0x40) | 0x10;
else
@@ -676,6 +688,7 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd,
v4l2_std_id std = decoder->norm;
u8 reg;
int pos, type = 0;
+ int i, ret = 0;
if (std == V4L2_STD_ALL) {
v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
@@ -690,13 +703,17 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd,
reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- pos = tvp5150_read(sd, reg) & 0x0f;
- if (pos < 0x0f)
- type = regs[pos].type.vbi_type;
-
- pos = tvp5150_read(sd, reg + 1) & 0x0f;
- if (pos < 0x0f)
- type |= regs[pos].type.vbi_type;
+ for (i = 0; i <= 1; i++) {
+ ret = tvp5150_read(sd, reg + i);
+ if (ret < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n",
+ __func__, ret);
+ return 0;
+ }
+ pos = ret & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
+ }
return type;
}
@@ -1031,13 +1048,21 @@ static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
+ int res;
+
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (!v4l2_chip_match_i2c_client(client, &reg->match))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- reg->val = tvp5150_read(sd, reg->reg & 0xff);
+ res = tvp5150_read(sd, reg->reg & 0xff);
+ if (res < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n", __func__, res);
+ return res;
+ }
+
+ reg->val = res;
reg->size = 1;
return 0;
}
@@ -1126,7 +1151,8 @@ static int tvp5150_probe(struct i2c_client *c,
{
struct tvp5150 *core;
struct v4l2_subdev *sd;
- u8 msb_id, lsb_id, msb_rom, lsb_rom;
+ int tvp5150_id[4];
+ int i, res;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
@@ -1139,26 +1165,37 @@ static int tvp5150_probe(struct i2c_client *c,
}
sd = &core->sd;
v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+
+ /*
+ * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
+ * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
+ */
+ for (i = 0; i < 4; i++) {
+ res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
+ if (res < 0)
+ goto free_core;
+ tvp5150_id[i] = res;
+ }
+
v4l_info(c, "chip found @ 0x%02x (%s)\n",
c->addr << 1, c->adapter->name);
- msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
- lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
- msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
- lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
-
- if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
- v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
+ if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n",
+ tvp5150_id[0], tvp5150_id[1]);
/* ITU-T BT.656.4 timing */
tvp5150_write(sd, TVP5150_REV_SELECT, 0);
} else {
- if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
- v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
+ /* Is TVP5150A */
+ if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
+ v4l2_info(sd, "tvp%02x%02xa detected.\n",
+ tvp5150_id[2], tvp5150_id[3]);
} else {
v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
- msb_id, lsb_id);
- v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
+ tvp5150_id[2], tvp5150_id[3]);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n",
+ tvp5150_id[2], tvp5150_id[3]);
}
}
@@ -1177,11 +1214,9 @@ static int tvp5150_probe(struct i2c_client *c,
V4L2_CID_HUE, -128, 127, 1, 0);
sd->ctrl_handler = &core->hdl;
if (core->hdl.error) {
- int err = core->hdl.error;
-
+ res = core->hdl.error;
v4l2_ctrl_handler_free(&core->hdl);
- kfree(core);
- return err;
+ goto free_core;
}
v4l2_ctrl_handler_setup(&core->hdl);
@@ -1197,6 +1232,10 @@ static int tvp5150_probe(struct i2c_client *c,
if (debug > 1)
tvp5150_log_status(sd);
return 0;
+
+free_core:
+ kfree(core);
+ return res;
}
static int tvp5150_remove(struct i2c_client *c)
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index 8768efb8508a..9f53eacb66e3 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -699,11 +699,9 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd,
struct tw9910_priv *priv = to_tw9910(client);
if (!priv->scale) {
- int ret;
- u32 width = 640, height = 480;
- ret = tw9910_set_frame(sd, &width, &height);
- if (ret < 0)
- return ret;
+ priv->scale = tw9910_select_norm(priv->norm, 640, 480);
+ if (!priv->scale)
+ return -EINVAL;
}
mf->width = priv->scale->width;
diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig
index 6c197da531b2..541c9f1e4c6a 100644
--- a/drivers/media/video/uvc/Kconfig
+++ b/drivers/media/video/uvc/Kconfig
@@ -10,6 +10,7 @@ config USB_VIDEO_CLASS
config USB_VIDEO_CLASS_INPUT_EVDEV
bool "UVC input events device support"
default y
+ depends on USB_VIDEO_CLASS
depends on USB_VIDEO_CLASS=INPUT || INPUT=y
---help---
This option makes USB Video Class devices register an input device
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index af26bbe6f76e..f7061a5ef1d2 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -2083,7 +2083,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
/* Walk the entities list and instantiate controls */
list_for_each_entry(entity, &dev->entities, list) {
struct uvc_control *ctrl;
- unsigned int bControlSize = 0, ncontrols = 0;
+ unsigned int bControlSize = 0, ncontrols;
__u8 *bmControls = NULL;
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
@@ -2101,8 +2101,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
uvc_ctrl_prune_entity(dev, entity);
/* Count supported controls and allocate the controls array */
- for (i = 0; i < bControlSize; ++i)
- ncontrols += hweight8(bmControls[i]);
+ ncontrols = memweight(bmControls, bControlSize);
if (ncontrols == 0)
continue;
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 759bef8897e9..f00db3060e0e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -1051,7 +1051,7 @@ static long uvc_v4l2_ioctl(struct file *file,
{
if (uvc_trace_param & UVC_TRACE_IOCTL) {
uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
- v4l_printk_ioctl(cmd);
+ v4l_printk_ioctl(NULL, cmd);
printk(")\n");
}
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index b76b0ac0958f..7ac4347ca09e 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -1188,7 +1188,11 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
u8 *mem;
int len, ret;
- if (urb->actual_length == 0)
+ /*
+ * Ignore ZLPs if they're not part of a frame, otherwise process them
+ * to trigger the end of payload detection.
+ */
+ if (urb->actual_length == 0 && stream->bulk.header_size == 0)
return;
mem = urb->transfer_buffer;
@@ -1594,7 +1598,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
if (psize >= bandwidth && psize <= best_psize) {
- altsetting = i;
+ altsetting = alts->desc.bAlternateSetting;
best_psize = psize;
best_ep = ep;
}
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 5327ad3a6390..9ebd5c540d10 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -327,7 +327,7 @@ struct v4l2_buffer32 {
compat_caddr_t planes;
} m;
__u32 length;
- __u32 input;
+ __u32 reserved2;
__u32 reserved;
};
@@ -387,8 +387,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
get_user(kp->index, &up->index) ||
get_user(kp->type, &up->type) ||
get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory) ||
- get_user(kp->input, &up->input))
+ get_user(kp->memory, &up->memory))
return -EFAULT;
if (V4L2_TYPE_IS_OUTPUT(kp->type))
@@ -472,8 +471,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->index, &up->index) ||
put_user(kp->type, &up->type) ||
put_user(kp->flags, &up->flags) ||
- put_user(kp->memory, &up->memory) ||
- put_user(kp->input, &up->input))
+ put_user(kp->memory, &up->memory))
return -EFAULT;
if (put_user(kp->bytesused, &up->bytesused) ||
@@ -482,6 +480,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
put_user(kp->sequence, &up->sequence) ||
+ put_user(kp->reserved2, &up->reserved2) ||
put_user(kp->reserved, &up->reserved))
return -EFAULT;
@@ -1026,6 +1025,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_ENUM_DV_TIMINGS:
case VIDIOC_QUERY_DV_TIMINGS:
case VIDIOC_DV_TIMINGS_CAP:
+ case VIDIOC_ENUM_FREQ_BANDS:
ret = do_video_ioctl(file, cmd, arg);
break;
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 9abd9abd4502..b6a2ee71e5c3 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -755,6 +755,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_HUE_AUTO:
case V4L2_CID_CHROMA_AGC:
case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_AUTOBRIGHTNESS:
case V4L2_CID_MPEG_AUDIO_MUTE:
case V4L2_CID_MPEG_VIDEO_MUTE:
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
@@ -2120,7 +2121,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
/* First zero the helper field in the master control references */
for (i = 0; i < cs->count; i++)
- helpers[i].mref->helper = 0;
+ helpers[i].mref->helper = NULL;
for (i = 0, h = helpers; i < cs->count; i++, h++) {
struct v4l2_ctrl_ref *mref = h->mref;
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 0cbada18f6f5..07aeafca9eaa 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -46,6 +46,29 @@ static ssize_t show_index(struct device *cd,
return sprintf(buf, "%i\n", vdev->index);
}
+static ssize_t show_debug(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%i\n", vdev->debug);
+}
+
+static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct video_device *vdev = to_video_device(cd);
+ int res = 0;
+ u16 value;
+
+ res = kstrtou16(buf, 0, &value);
+ if (res)
+ return res;
+
+ vdev->debug = value;
+ return len;
+}
+
static ssize_t show_name(struct device *cd,
struct device_attribute *attr, char *buf)
{
@@ -56,6 +79,7 @@ static ssize_t show_name(struct device *cd,
static struct device_attribute video_device_attrs[] = {
__ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(debug, 0644, show_debug, set_debug),
__ATTR(index, S_IRUGO, show_index, NULL),
__ATTR_NULL
};
@@ -281,6 +305,9 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
ret = vdev->fops->read(filp, buf, sz, off);
if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+ video_device_node_name(vdev), sz, ret);
return ret;
}
@@ -299,6 +326,9 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
ret = vdev->fops->write(filp, buf, sz, off);
if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+ video_device_node_name(vdev), sz, ret);
return ret;
}
@@ -315,6 +345,9 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
ret = vdev->fops->poll(filp, poll);
if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: poll: %08x\n",
+ video_device_node_name(vdev), ret);
return ret;
}
@@ -324,20 +357,14 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret = -ENODEV;
if (vdev->fops->unlocked_ioctl) {
- bool locked = false;
+ struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
- if (vdev->lock) {
- /* always lock unless the cmd is marked as "don't use lock" */
- locked = !v4l2_is_known_ioctl(cmd) ||
- !test_bit(_IOC_NR(cmd), vdev->disable_locking);
-
- if (locked && mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- }
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (locked)
- mutex_unlock(vdev->lock);
+ if (lock)
+ mutex_unlock(lock);
} else if (vdev->fops->ioctl) {
/* This code path is a replacement for the BKL. It is a major
* hack but it will have to do for those drivers that are not
@@ -385,12 +412,17 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp,
unsigned long flags)
{
struct video_device *vdev = video_devdata(filp);
+ int ret;
if (!vdev->fops->get_unmapped_area)
return -ENOSYS;
if (!video_is_registered(vdev))
return -ENODEV;
- return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+ ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+ video_device_node_name(vdev), ret);
+ return ret;
}
#endif
@@ -408,6 +440,9 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
ret = vdev->fops->mmap(filp, vm);
if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: mmap (%d)\n",
+ video_device_node_name(vdev), ret);
return ret;
}
@@ -443,6 +478,9 @@ static int v4l2_open(struct inode *inode, struct file *filp)
}
err:
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: open (%d)\n",
+ video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
@@ -462,6 +500,9 @@ static int v4l2_release(struct inode *inode, struct file *filp)
if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
}
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: release\n",
+ video_device_node_name(vdev));
/* decrease the refcount unconditionally since the release()
return value is ignored. */
video_put(vdev);
@@ -656,7 +697,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
- if (ops->vidioc_g_parm || vdev->vfl_type == VFL_TYPE_GRABBER)
+ if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
+ (ops->vidioc_g_std || vdev->tvnorms)))
set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
@@ -688,6 +730,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+ if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
+ set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
BASE_VIDIOC_PRIVATE);
}
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index d7fa8962d8b3..c3b7b5f59b32 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -27,27 +27,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
-
-#define dbgarg(cmd, fmt, arg...) \
- do { \
- if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \
- printk(KERN_DEBUG "%s: ", vfd->name); \
- v4l_printk_ioctl(cmd); \
- printk(" " fmt, ## arg); \
- } \
- } while (0)
-
-#define dbgarg2(fmt, arg...) \
- do { \
- if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \
- printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\
- } while (0)
-
-#define dbgarg3(fmt, arg...) \
- do { \
- if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \
- printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\
- } while (0)
+#include <media/videobuf2-core.h>
/* Zero out the end of the struct pointed to by p. Everything after, but
* not including, the specified field is cleared. */
@@ -183,207 +163,507 @@ static const char *v4l2_memory_names[] = {
/* ------------------------------------------------------------------ */
/* debug help functions */
-struct v4l2_ioctl_info {
- unsigned int ioctl;
- u16 flags;
- const char * const name;
-};
+static void v4l_print_querycap(const void *arg, bool write_only)
+{
+ const struct v4l2_capability *p = arg;
-/* This control needs a priority check */
-#define INFO_FL_PRIO (1 << 0)
-/* This control can be valid if the filehandle passes a control handler. */
-#define INFO_FL_CTRL (1 << 1)
+ pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
+ "capabilities=0x%08x, device_caps=0x%08x\n",
+ p->driver, p->card, p->bus_info,
+ p->version, p->capabilities, p->device_caps);
+}
-#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags, \
- .name = #_ioctl, \
+static void v4l_print_enuminput(const void *arg, bool write_only)
+{
+ const struct v4l2_input *p = arg;
+
+ pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
+ "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
+ p->index, p->name, p->type, p->audioset, p->tuner,
+ (unsigned long long)p->std, p->status, p->capabilities);
}
-static struct v4l2_ioctl_info v4l2_ioctls[] = {
- IOCTL_INFO(VIDIOC_QUERYCAP, 0),
- IOCTL_INFO(VIDIOC_ENUM_FMT, 0),
- IOCTL_INFO(VIDIOC_G_FMT, 0),
- IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_QUERYBUF, 0),
- IOCTL_INFO(VIDIOC_G_FBUF, 0),
- IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_QBUF, 0),
- IOCTL_INFO(VIDIOC_DQBUF, 0),
- IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_PARM, 0),
- IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_STD, 0),
- IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_ENUMSTD, 0),
- IOCTL_INFO(VIDIOC_ENUMINPUT, 0),
- IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_G_TUNER, 0),
- IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_AUDIO, 0),
- IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_G_INPUT, 0),
- IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_OUTPUT, 0),
- IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0),
- IOCTL_INFO(VIDIOC_G_AUDOUT, 0),
- IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_MODULATOR, 0),
- IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_FREQUENCY, 0),
- IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_CROPCAP, 0),
- IOCTL_INFO(VIDIOC_G_CROP, 0),
- IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_SELECTION, 0),
- IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0),
- IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_QUERYSTD, 0),
- IOCTL_INFO(VIDIOC_TRY_FMT, 0),
- IOCTL_INFO(VIDIOC_ENUMAUDIO, 0),
- IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0),
- IOCTL_INFO(VIDIOC_G_PRIORITY, 0),
- IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0),
- IOCTL_INFO(VIDIOC_LOG_STATUS, 0),
- IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0),
- IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0),
- IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0),
- IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0),
- IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0),
- IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0),
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0),
- IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0),
-#endif
- IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0),
- IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0),
- IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_DV_PRESET, 0),
- IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0),
- IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0),
- IOCTL_INFO(VIDIOC_DQEVENT, 0),
- IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0),
- IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0),
- IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO),
- IOCTL_INFO(VIDIOC_PREPARE_BUF, 0),
- IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0),
- IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0),
- IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0),
-};
-#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+static void v4l_print_enumoutput(const void *arg, bool write_only)
+{
+ const struct v4l2_output *p = arg;
-bool v4l2_is_known_ioctl(unsigned int cmd)
+ pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
+ "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
+ p->index, p->name, p->type, p->audioset, p->modulator,
+ (unsigned long long)p->std, p->capabilities);
+}
+
+static void v4l_print_audio(const void *arg, bool write_only)
{
- if (_IOC_NR(cmd) >= V4L2_IOCTLS)
- return false;
- return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+ const struct v4l2_audio *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+ p->index, p->name, p->capability, p->mode);
}
-/* Common ioctl debug function. This function can be used by
- external ioctl messages as well as internal V4L ioctl */
-void v4l_printk_ioctl(unsigned int cmd)
+static void v4l_print_audioout(const void *arg, bool write_only)
{
- char *dir, *type;
+ const struct v4l2_audioout *p = arg;
- switch (_IOC_TYPE(cmd)) {
- case 'd':
- type = "v4l2_int";
+ if (write_only)
+ pr_cont("index=%u\n", p->index);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+ p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_fmtdesc(const void *arg, bool write_only)
+{
+ const struct v4l2_fmtdesc *p = arg;
+
+ pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
+ p->index, prt_names(p->type, v4l2_type_names),
+ p->flags, (p->pixelformat & 0xff),
+ (p->pixelformat >> 8) & 0xff,
+ (p->pixelformat >> 16) & 0xff,
+ (p->pixelformat >> 24) & 0xff,
+ p->description);
+}
+
+static void v4l_print_format(const void *arg, bool write_only)
+{
+ const struct v4l2_format *p = arg;
+ const struct v4l2_pix_format *pix;
+ const struct v4l2_pix_format_mplane *mp;
+ const struct v4l2_vbi_format *vbi;
+ const struct v4l2_sliced_vbi_format *sliced;
+ const struct v4l2_window *win;
+ const struct v4l2_clip *clip;
+ unsigned i;
+
+ pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ pix = &p->fmt.pix;
+ pr_cont(", width=%u, height=%u, "
+ "pixelformat=%c%c%c%c, field=%s, "
+ "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+ pix->width, pix->height,
+ (pix->pixelformat & 0xff),
+ (pix->pixelformat >> 8) & 0xff,
+ (pix->pixelformat >> 16) & 0xff,
+ (pix->pixelformat >> 24) & 0xff,
+ prt_names(pix->field, v4l2_field_names),
+ pix->bytesperline, pix->sizeimage,
+ pix->colorspace);
break;
- case 'V':
- if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
- type = "v4l2";
- break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ mp = &p->fmt.pix_mp;
+ pr_cont(", width=%u, height=%u, "
+ "format=%c%c%c%c, field=%s, "
+ "colorspace=%d, num_planes=%u\n",
+ mp->width, mp->height,
+ (mp->pixelformat & 0xff),
+ (mp->pixelformat >> 8) & 0xff,
+ (mp->pixelformat >> 16) & 0xff,
+ (mp->pixelformat >> 24) & 0xff,
+ prt_names(mp->field, v4l2_field_names),
+ mp->colorspace, mp->num_planes);
+ for (i = 0; i < mp->num_planes; i++)
+ printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
+ mp->plane_fmt[i].bytesperline,
+ mp->plane_fmt[i].sizeimage);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ win = &p->fmt.win;
+ pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
+ "chromakey=0x%08x, bitmap=%p, "
+ "global_alpha=0x%02x\n",
+ win->w.width, win->w.height,
+ win->w.left, win->w.top,
+ prt_names(win->field, v4l2_field_names),
+ win->chromakey, win->bitmap, win->global_alpha);
+ clip = win->clips;
+ for (i = 0; i < win->clipcount; i++) {
+ printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
+ i, clip->c.width, clip->c.height,
+ clip->c.left, clip->c.top);
+ clip = clip->next;
}
- printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
- return;
- default:
- type = "unknown";
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ vbi = &p->fmt.vbi;
+ pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
+ "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
+ vbi->sampling_rate, vbi->offset,
+ vbi->samples_per_line,
+ (vbi->sample_format & 0xff),
+ (vbi->sample_format >> 8) & 0xff,
+ (vbi->sample_format >> 16) & 0xff,
+ (vbi->sample_format >> 24) & 0xff,
+ vbi->start[0], vbi->start[1],
+ vbi->count[0], vbi->count[1]);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ sliced = &p->fmt.sliced;
+ pr_cont(", service_set=0x%08x, io_size=%d\n",
+ sliced->service_set, sliced->io_size);
+ for (i = 0; i < 24; i++)
+ printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+ sliced->service_lines[0][i],
+ sliced->service_lines[1][i]);
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ pr_cont("\n");
+ break;
}
+}
- switch (_IOC_DIR(cmd)) {
- case _IOC_NONE: dir = "--"; break;
- case _IOC_READ: dir = "r-"; break;
- case _IOC_WRITE: dir = "-w"; break;
- case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
- default: dir = "*ERR*"; break;
- }
- printk("%s ioctl '%c', dir=%s, #%d (0x%08x)",
- type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+static void v4l_print_framebuffer(const void *arg, bool write_only)
+{
+ const struct v4l2_framebuffer *p = arg;
+
+ pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
+ "height=%u, pixelformat=%c%c%c%c, "
+ "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+ p->capability, p->flags, p->base,
+ p->fmt.width, p->fmt.height,
+ (p->fmt.pixelformat & 0xff),
+ (p->fmt.pixelformat >> 8) & 0xff,
+ (p->fmt.pixelformat >> 16) & 0xff,
+ (p->fmt.pixelformat >> 24) & 0xff,
+ p->fmt.bytesperline, p->fmt.sizeimage,
+ p->fmt.colorspace);
+}
+
+static void v4l_print_buftype(const void *arg, bool write_only)
+{
+ pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
+}
+
+static void v4l_print_modulator(const void *arg, bool write_only)
+{
+ const struct v4l2_modulator *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
+ p->index, p->name, p->capability,
+ p->rangelow, p->rangehigh, p->txsubchans);
+}
+
+static void v4l_print_tuner(const void *arg, bool write_only)
+{
+ const struct v4l2_tuner *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
+ else
+ pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
+ "rxsubchans=0x%x, audmode=%u\n",
+ p->index, p->name, p->type,
+ p->capability, p->rangelow,
+ p->rangehigh, p->signal, p->afc,
+ p->rxsubchans, p->audmode);
}
-EXPORT_SYMBOL(v4l_printk_ioctl);
-static void dbgbuf(unsigned int cmd, struct video_device *vfd,
- struct v4l2_buffer *p)
+static void v4l_print_frequency(const void *arg, bool write_only)
{
- struct v4l2_timecode *tc = &p->timecode;
- struct v4l2_plane *plane;
+ const struct v4l2_frequency *p = arg;
+
+ pr_cont("tuner=%u, type=%u, frequency=%u\n",
+ p->tuner, p->type, p->frequency);
+}
+
+static void v4l_print_standard(const void *arg, bool write_only)
+{
+ const struct v4l2_standard *p = arg;
+
+ pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
+ "framelines=%u\n", p->index,
+ (unsigned long long)p->id, p->name,
+ p->frameperiod.numerator,
+ p->frameperiod.denominator,
+ p->framelines);
+}
+
+static void v4l_print_std(const void *arg, bool write_only)
+{
+ pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
+}
+
+static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
+{
+ const struct v4l2_hw_freq_seek *p = arg;
+
+ pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
+ p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
+}
+
+static void v4l_print_requestbuffers(const void *arg, bool write_only)
+{
+ const struct v4l2_requestbuffers *p = arg;
+
+ pr_cont("count=%d, type=%s, memory=%s\n",
+ p->count,
+ prt_names(p->type, v4l2_type_names),
+ prt_names(p->memory, v4l2_memory_names));
+}
+
+static void v4l_print_buffer(const void *arg, bool write_only)
+{
+ const struct v4l2_buffer *p = arg;
+ const struct v4l2_timecode *tc = &p->timecode;
+ const struct v4l2_plane *plane;
int i;
- dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
- "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n",
+ pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
+ "flags=0x%08x, field=%s, sequence=%d, memory=%s",
p->timestamp.tv_sec / 3600,
(int)(p->timestamp.tv_sec / 60) % 60,
(int)(p->timestamp.tv_sec % 60),
(long)p->timestamp.tv_usec,
p->index,
prt_names(p->type, v4l2_type_names),
- p->flags, p->field, p->sequence,
- prt_names(p->memory, v4l2_memory_names));
+ p->flags, prt_names(p->field, v4l2_field_names),
+ p->sequence, prt_names(p->memory, v4l2_memory_names));
if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+ pr_cont("\n");
for (i = 0; i < p->length; ++i) {
plane = &p->m.planes[i];
- dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x "
- "offset/userptr=0x%08lx, length=%d\n",
+ printk(KERN_DEBUG
+ "plane %d: bytesused=%d, data_offset=0x%08x "
+ "offset/userptr=0x%lx, length=%d\n",
i, plane->bytesused, plane->data_offset,
plane->m.userptr, plane->length);
}
} else {
- dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n",
+ pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
p->bytesused, p->m.userptr, p->length);
}
- dbgarg2("timecode=%02d:%02d:%02d type=%d, "
- "flags=0x%08d, frames=%d, userbits=0x%08x\n",
+ printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
+ "flags=0x%08x, frames=%d, userbits=0x%08x\n",
tc->hours, tc->minutes, tc->seconds,
tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
}
-static inline void dbgrect(struct video_device *vfd, char *s,
- struct v4l2_rect *r)
+static void v4l_print_create_buffers(const void *arg, bool write_only)
{
- dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top,
- r->width, r->height);
-};
+ const struct v4l2_create_buffers *p = arg;
+
+ pr_cont("index=%d, count=%d, memory=%s, ",
+ p->index, p->count,
+ prt_names(p->memory, v4l2_memory_names));
+ v4l_print_format(&p->format, write_only);
+}
-static void dbgtimings(struct video_device *vfd,
- const struct v4l2_dv_timings *p)
+static void v4l_print_streamparm(const void *arg, bool write_only)
{
+ const struct v4l2_streamparm *p = arg;
+
+ pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+
+ if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ const struct v4l2_captureparm *c = &p->parm.capture;
+
+ pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
+ "extendedmode=%d, readbuffers=%d\n",
+ c->capability, c->capturemode,
+ c->timeperframe.numerator, c->timeperframe.denominator,
+ c->extendedmode, c->readbuffers);
+ } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ const struct v4l2_outputparm *c = &p->parm.output;
+
+ pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
+ "extendedmode=%d, writebuffers=%d\n",
+ c->capability, c->outputmode,
+ c->timeperframe.numerator, c->timeperframe.denominator,
+ c->extendedmode, c->writebuffers);
+ }
+}
+
+static void v4l_print_queryctrl(const void *arg, bool write_only)
+{
+ const struct v4l2_queryctrl *p = arg;
+
+ pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+ "step=%d, default=%d, flags=0x%08x\n",
+ p->id, p->type, p->name,
+ p->minimum, p->maximum,
+ p->step, p->default_value, p->flags);
+}
+
+static void v4l_print_querymenu(const void *arg, bool write_only)
+{
+ const struct v4l2_querymenu *p = arg;
+
+ pr_cont("id=0x%x, index=%d\n", p->id, p->index);
+}
+
+static void v4l_print_control(const void *arg, bool write_only)
+{
+ const struct v4l2_control *p = arg;
+
+ pr_cont("id=0x%x, value=%d\n", p->id, p->value);
+}
+
+static void v4l_print_ext_controls(const void *arg, bool write_only)
+{
+ const struct v4l2_ext_controls *p = arg;
+ int i;
+
+ pr_cont("class=0x%x, count=%d, error_idx=%d",
+ p->ctrl_class, p->count, p->error_idx);
+ for (i = 0; i < p->count; i++) {
+ if (p->controls[i].size)
+ pr_cont(", id/val=0x%x/0x%x",
+ p->controls[i].id, p->controls[i].value);
+ else
+ pr_cont(", id/size=0x%x/%u",
+ p->controls[i].id, p->controls[i].size);
+ }
+ pr_cont("\n");
+}
+
+static void v4l_print_cropcap(const void *arg, bool write_only)
+{
+ const struct v4l2_cropcap *p = arg;
+
+ pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
+ "defrect wxh=%dx%d, x,y=%d,%d\n, "
+ "pixelaspect %d/%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->bounds.width, p->bounds.height,
+ p->bounds.left, p->bounds.top,
+ p->defrect.width, p->defrect.height,
+ p->defrect.left, p->defrect.top,
+ p->pixelaspect.numerator, p->pixelaspect.denominator);
+}
+
+static void v4l_print_crop(const void *arg, bool write_only)
+{
+ const struct v4l2_crop *p = arg;
+
+ pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->c.width, p->c.height,
+ p->c.left, p->c.top);
+}
+
+static void v4l_print_selection(const void *arg, bool write_only)
+{
+ const struct v4l2_selection *p = arg;
+
+ pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->target, p->flags,
+ p->r.width, p->r.height, p->r.left, p->r.top);
+}
+
+static void v4l_print_jpegcompression(const void *arg, bool write_only)
+{
+ const struct v4l2_jpegcompression *p = arg;
+
+ pr_cont("quality=%d, APPn=%d, APP_len=%d, "
+ "COM_len=%d, jpeg_markers=0x%x\n",
+ p->quality, p->APPn, p->APP_len,
+ p->COM_len, p->jpeg_markers);
+}
+
+static void v4l_print_enc_idx(const void *arg, bool write_only)
+{
+ const struct v4l2_enc_idx *p = arg;
+
+ pr_cont("entries=%d, entries_cap=%d\n",
+ p->entries, p->entries_cap);
+}
+
+static void v4l_print_encoder_cmd(const void *arg, bool write_only)
+{
+ const struct v4l2_encoder_cmd *p = arg;
+
+ pr_cont("cmd=%d, flags=0x%x\n",
+ p->cmd, p->flags);
+}
+
+static void v4l_print_decoder_cmd(const void *arg, bool write_only)
+{
+ const struct v4l2_decoder_cmd *p = arg;
+
+ pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
+
+ if (p->cmd == V4L2_DEC_CMD_START)
+ pr_info("speed=%d, format=%u\n",
+ p->start.speed, p->start.format);
+ else if (p->cmd == V4L2_DEC_CMD_STOP)
+ pr_info("pts=%llu\n", p->stop.pts);
+}
+
+static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
+{
+ const struct v4l2_dbg_chip_ident *p = arg;
+
+ pr_cont("type=%u, ", p->match.type);
+ if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ pr_cont("name=%s, ", p->match.name);
+ else
+ pr_cont("addr=%u, ", p->match.addr);
+ pr_cont("chip_ident=%u, revision=0x%x\n",
+ p->ident, p->revision);
+}
+
+static void v4l_print_dbg_register(const void *arg, bool write_only)
+{
+ const struct v4l2_dbg_register *p = arg;
+
+ pr_cont("type=%u, ", p->match.type);
+ if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ pr_cont("name=%s, ", p->match.name);
+ else
+ pr_cont("addr=%u, ", p->match.addr);
+ pr_cont("reg=0x%llx, val=0x%llx\n",
+ p->reg, p->val);
+}
+
+static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_enum_preset *p = arg;
+
+ pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
+ p->index, p->preset, p->name, p->width, p->height);
+}
+
+static void v4l_print_dv_preset(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_preset *p = arg;
+
+ pr_cont("preset=%u\n", p->preset);
+}
+
+static void v4l_print_dv_timings(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_timings *p = arg;
+
switch (p->type) {
case V4L2_DV_BT_656_1120:
- dbgarg2("bt-656/1120:interlaced=%d,"
- " pixelclock=%lld,"
- " width=%d, height=%d, polarities=%x,"
- " hfrontporch=%d, hsync=%d,"
- " hbackporch=%d, vfrontporch=%d,"
- " vsync=%d, vbackporch=%d,"
- " il_vfrontporch=%d, il_vsync=%d,"
- " il_vbackporch=%d, standards=%x, flags=%x\n",
+ pr_cont("type=bt-656/1120, interlaced=%u, "
+ "pixelclock=%llu, "
+ "width=%u, height=%u, polarities=0x%x, "
+ "hfrontporch=%u, hsync=%u, "
+ "hbackporch=%u, vfrontporch=%u, "
+ "vsync=%u, vbackporch=%u, "
+ "il_vfrontporch=%u, il_vsync=%u, "
+ "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
p->bt.interlaced, p->bt.pixelclock,
p->bt.width, p->bt.height,
p->bt.polarities, p->bt.hfrontporch,
@@ -394,67 +674,184 @@ static void dbgtimings(struct video_device *vfd,
p->bt.standards, p->bt.flags);
break;
default:
- dbgarg2("Unknown type %d!\n", p->type);
+ pr_cont("type=%d\n", p->type);
break;
}
}
-static inline void v4l_print_pix_fmt(struct video_device *vfd,
- struct v4l2_pix_format *fmt)
+static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
{
- dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
- "bytesperline=%d sizeimage=%d, colorspace=%d\n",
- fmt->width, fmt->height,
- (fmt->pixelformat & 0xff),
- (fmt->pixelformat >> 8) & 0xff,
- (fmt->pixelformat >> 16) & 0xff,
- (fmt->pixelformat >> 24) & 0xff,
- prt_names(fmt->field, v4l2_field_names),
- fmt->bytesperline, fmt->sizeimage, fmt->colorspace);
-};
+ const struct v4l2_enum_dv_timings *p = arg;
+
+ pr_cont("index=%u, ", p->index);
+ v4l_print_dv_timings(&p->timings, write_only);
+}
-static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd,
- struct v4l2_pix_format_mplane *fmt)
+static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
{
- int i;
+ const struct v4l2_dv_timings_cap *p = arg;
- dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
- "colorspace=%d, num_planes=%d\n",
- fmt->width, fmt->height,
- (fmt->pixelformat & 0xff),
- (fmt->pixelformat >> 8) & 0xff,
- (fmt->pixelformat >> 16) & 0xff,
- (fmt->pixelformat >> 24) & 0xff,
- prt_names(fmt->field, v4l2_field_names),
- fmt->colorspace, fmt->num_planes);
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
+ "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
+ p->bt.min_width, p->bt.max_width,
+ p->bt.min_height, p->bt.max_height,
+ p->bt.min_pixelclock, p->bt.max_pixelclock,
+ p->bt.standards, p->bt.capabilities);
+ break;
+ default:
+ pr_cont("type=%u\n", p->type);
+ break;
+ }
+}
- for (i = 0; i < fmt->num_planes; ++i)
- dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i,
- fmt->plane_fmt[i].bytesperline,
- fmt->plane_fmt[i].sizeimage);
+static void v4l_print_frmsizeenum(const void *arg, bool write_only)
+{
+ const struct v4l2_frmsizeenum *p = arg;
+
+ pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
+ p->index,
+ (p->pixel_format & 0xff),
+ (p->pixel_format >> 8) & 0xff,
+ (p->pixel_format >> 16) & 0xff,
+ (p->pixel_format >> 24) & 0xff,
+ p->type);
+ switch (p->type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ pr_cont(" wxh=%ux%u\n",
+ p->discrete.width, p->discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
+ p->stepwise.min_width, p->stepwise.min_height,
+ p->stepwise.step_width, p->stepwise.step_height,
+ p->stepwise.max_width, p->stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ /* fall through */
+ default:
+ pr_cont("\n");
+ break;
+ }
}
-static inline void v4l_print_ext_ctrls(unsigned int cmd,
- struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
+static void v4l_print_frmivalenum(const void *arg, bool write_only)
{
- __u32 i;
+ const struct v4l2_frmivalenum *p = arg;
- if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG))
- return;
- dbgarg(cmd, "");
- printk(KERN_CONT "class=0x%x", c->ctrl_class);
- for (i = 0; i < c->count; i++) {
- if (show_vals && !c->controls[i].size)
- printk(KERN_CONT " id/val=0x%x/0x%x",
- c->controls[i].id, c->controls[i].value);
+ pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
+ p->index,
+ (p->pixel_format & 0xff),
+ (p->pixel_format >> 8) & 0xff,
+ (p->pixel_format >> 16) & 0xff,
+ (p->pixel_format >> 24) & 0xff,
+ p->width, p->height, p->type);
+ switch (p->type) {
+ case V4L2_FRMIVAL_TYPE_DISCRETE:
+ pr_cont(" fps=%d/%d\n",
+ p->discrete.numerator,
+ p->discrete.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_STEPWISE:
+ pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
+ p->stepwise.min.numerator,
+ p->stepwise.min.denominator,
+ p->stepwise.max.numerator,
+ p->stepwise.max.denominator,
+ p->stepwise.step.numerator,
+ p->stepwise.step.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+ /* fall through */
+ default:
+ pr_cont("\n");
+ break;
+ }
+}
+
+static void v4l_print_event(const void *arg, bool write_only)
+{
+ const struct v4l2_event *p = arg;
+ const struct v4l2_event_ctrl *c;
+
+ pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
+ "timestamp=%lu.%9.9lu\n",
+ p->type, p->pending, p->sequence, p->id,
+ p->timestamp.tv_sec, p->timestamp.tv_nsec);
+ switch (p->type) {
+ case V4L2_EVENT_VSYNC:
+ printk(KERN_DEBUG "field=%s\n",
+ prt_names(p->u.vsync.field, v4l2_field_names));
+ break;
+ case V4L2_EVENT_CTRL:
+ c = &p->u.ctrl;
+ printk(KERN_DEBUG "changes=0x%x, type=%u, ",
+ c->changes, c->type);
+ if (c->type == V4L2_CTRL_TYPE_INTEGER64)
+ pr_cont("value64=%lld, ", c->value64);
else
- printk(KERN_CONT " id=0x%x,size=%u",
- c->controls[i].id, c->controls[i].size);
+ pr_cont("value=%d, ", c->value);
+ pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
+ " default_value=%d\n",
+ c->flags, c->minimum, c->maximum,
+ c->step, c->default_value);
+ break;
+ case V4L2_EVENT_FRAME_SYNC:
+ pr_cont("frame_sequence=%u\n",
+ p->u.frame_sync.frame_sequence);
+ break;
}
- printk(KERN_CONT "\n");
-};
+}
+
+static void v4l_print_event_subscription(const void *arg, bool write_only)
+{
+ const struct v4l2_event_subscription *p = arg;
-static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+ pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
+ p->type, p->id, p->flags);
+}
+
+static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
+{
+ const struct v4l2_sliced_vbi_cap *p = arg;
+ int i;
+
+ pr_cont("type=%s, service_set=0x%08x\n",
+ prt_names(p->type, v4l2_type_names), p->service_set);
+ for (i = 0; i < 24; i++)
+ printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+ p->service_lines[0][i],
+ p->service_lines[1][i]);
+}
+
+static void v4l_print_freq_band(const void *arg, bool write_only)
+{
+ const struct v4l2_frequency_band *p = arg;
+
+ pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
+ p->tuner, p->type, p->index,
+ p->capability, p->rangelow,
+ p->rangehigh, p->modulation);
+}
+
+static void v4l_print_u32(const void *arg, bool write_only)
+{
+ pr_cont("value=%u\n", *(const u32 *)arg);
+}
+
+static void v4l_print_newline(const void *arg, bool write_only)
+{
+ pr_cont("\n");
+}
+
+static void v4l_print_default(const void *arg, bool write_only)
+{
+ pr_cont("driver-specific ioctl\n");
+}
+
+static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
{
__u32 i;
@@ -536,1615 +933,1234 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
return -EINVAL;
}
-static long __video_do_ioctl(struct file *file,
- unsigned int cmd, void *arg)
+static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
{
- struct video_device *vfd = video_devdata(file);
- const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
- void *fh = file->private_data;
- struct v4l2_fh *vfh = NULL;
- int use_fh_prio = 0;
- long ret = -ENOTTY;
-
- if (ops == NULL) {
- printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
- vfd->name);
- return ret;
- }
+ struct v4l2_capability *cap = (struct v4l2_capability *)arg;
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
- vfh = file->private_data;
- use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- }
+ cap->version = LINUX_VERSION_CODE;
+ return ops->vidioc_querycap(file, fh, cap);
+}
- if (v4l2_is_known_ioctl(cmd)) {
- struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];
+static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
+}
- if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
- !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
- return -ENOTTY;
+static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
+}
- if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
- ret = v4l2_prio_check(vfd->prio, vfh->prio);
- if (ret)
- return ret;
- }
- }
+static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd;
+ u32 *p = arg;
- if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
- !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
- v4l_print_ioctl(vfd->name, cmd);
- printk(KERN_CONT "\n");
- }
+ if (ops->vidioc_g_priority)
+ return ops->vidioc_g_priority(file, fh, arg);
+ vfd = video_devdata(file);
+ *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+ return 0;
+}
- switch (cmd) {
+static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd;
+ struct v4l2_fh *vfh;
+ u32 *p = arg;
+
+ if (ops->vidioc_s_priority)
+ return ops->vidioc_s_priority(file, fh, *p);
+ vfd = video_devdata(file);
+ vfh = file->private_data;
+ return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+}
- /* --- capabilities ------------------------------------------ */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = (struct v4l2_capability *)arg;
-
- cap->version = LINUX_VERSION_CODE;
- ret = ops->vidioc_querycap(file, fh, cap);
- if (!ret)
- dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
- "version=0x%08x, "
- "capabilities=0x%08x, "
- "device_caps=0x%08x\n",
- cap->driver, cap->card, cap->bus_info,
- cap->version,
- cap->capabilities,
- cap->device_caps);
- break;
- }
+static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_input *p = arg;
- /* --- priority ------------------------------------------ */
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific input, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_IN_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_IN_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
+
+ return ops->vidioc_enum_input(file, fh, p);
+}
- if (ops->vidioc_g_priority) {
- ret = ops->vidioc_g_priority(file, fh, p);
- } else if (use_fh_prio) {
- *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
- ret = 0;
- }
- if (!ret)
- dbgarg(cmd, "priority is %d\n", *p);
- break;
- }
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_output *p = arg;
- dbgarg(cmd, "setting priority to %d\n", *p);
- if (ops->vidioc_s_priority)
- ret = ops->vidioc_s_priority(file, fh, *p);
- else
- ret = v4l2_prio_change(&vfd->v4l2_dev->prio,
- &vfh->prio, *p);
- break;
- }
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific output, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_OUT_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
+
+ return ops->vidioc_enum_output(file, fh, p);
+}
- /* --- capture ioctls ---------------------------------------- */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
+static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_fmtdesc *p = arg;
- ret = -EINVAL;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (likely(ops->vidioc_enum_fmt_vid_cap))
- ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
- break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (likely(ops->vidioc_enum_fmt_vid_cap_mplane))
- ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
- fh, f);
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (likely(ops->vidioc_enum_fmt_vid_overlay))
- ret = ops->vidioc_enum_fmt_vid_overlay(file,
- fh, f);
+ return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (likely(ops->vidioc_enum_fmt_vid_out))
- ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
+ return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (likely(ops->vidioc_enum_fmt_vid_out_mplane))
- ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
- fh, f);
+ return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!ops->vidioc_enum_fmt_vid_out))
break;
- case V4L2_BUF_TYPE_PRIVATE:
- if (likely(ops->vidioc_enum_fmt_type_private))
- ret = ops->vidioc_enum_fmt_type_private(file,
- fh, f);
+ return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
break;
- default:
+ return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (unlikely(!ops->vidioc_enum_fmt_type_private))
break;
- }
- if (likely(!ret))
- dbgarg(cmd, "index=%d, type=%d, flags=%d, "
- "pixelformat=%c%c%c%c, description='%s'\n",
- f->index, f->type, f->flags,
- (f->pixelformat & 0xff),
- (f->pixelformat >> 8) & 0xff,
- (f->pixelformat >> 16) & 0xff,
- (f->pixelformat >> 24) & 0xff,
- f->description);
- break;
+ return ops->vidioc_enum_fmt_type_private(file, fh, arg);
}
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = (struct v4l2_format *)arg;
-
- /* FIXME: Should be one dump per type */
- dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
-
- ret = -EINVAL;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (ops->vidioc_g_fmt_vid_cap)
- ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
- if (!ret)
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ return -EINVAL;
+}
+
+static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!ops->vidioc_g_fmt_vid_cap))
break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (ops->vidioc_g_fmt_vid_cap_mplane)
- ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
- fh, f);
- if (!ret)
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (likely(ops->vidioc_g_fmt_vid_overlay))
- ret = ops->vidioc_g_fmt_vid_overlay(file,
- fh, f);
+ return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (ops->vidioc_g_fmt_vid_out)
- ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
- if (!ret)
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!ops->vidioc_g_fmt_vid_out))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (ops->vidioc_g_fmt_vid_out_mplane)
- ret = ops->vidioc_g_fmt_vid_out_mplane(file,
- fh, f);
- if (!ret)
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ return ops->vidioc_g_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (likely(ops->vidioc_g_fmt_vid_out_overlay))
- ret = ops->vidioc_g_fmt_vid_out_overlay(file,
- fh, f);
+ return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (likely(ops->vidioc_g_fmt_vbi_cap))
- ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f);
+ return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (likely(ops->vidioc_g_fmt_vbi_out))
- ret = ops->vidioc_g_fmt_vbi_out(file, fh, f);
+ return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_g_fmt_vbi_out))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (likely(ops->vidioc_g_fmt_sliced_vbi_cap))
- ret = ops->vidioc_g_fmt_sliced_vbi_cap(file,
- fh, f);
+ return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (likely(ops->vidioc_g_fmt_sliced_vbi_out))
- ret = ops->vidioc_g_fmt_sliced_vbi_out(file,
- fh, f);
+ return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
break;
- case V4L2_BUF_TYPE_PRIVATE:
- if (likely(ops->vidioc_g_fmt_type_private))
- ret = ops->vidioc_g_fmt_type_private(file,
- fh, f);
+ return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (unlikely(!ops->vidioc_g_fmt_type_private))
break;
- }
- break;
+ return ops->vidioc_g_fmt_type_private(file, fh, arg);
}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = (struct v4l2_format *)arg;
-
- ret = -EINVAL;
+ return -EINVAL;
+}
- /* FIXME: Should be one dump per type */
- dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.pix);
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_cap)
- ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!ops->vidioc_s_fmt_vid_cap))
break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
- if (ops->vidioc_s_fmt_vid_cap_mplane)
- ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_s_fmt_vid_overlay)
- ret = ops->vidioc_s_fmt_vid_overlay(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.pix);
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
- if (ops->vidioc_s_fmt_vid_out)
- ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!ops->vidioc_s_fmt_vid_out))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
- if (ops->vidioc_s_fmt_vid_out_mplane)
- ret = ops->vidioc_s_fmt_vid_out_mplane(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_s_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- CLEAR_AFTER_FIELD(f, fmt.win);
- if (ops->vidioc_s_fmt_vid_out_overlay)
- ret = ops->vidioc_s_fmt_vid_out_overlay(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (likely(ops->vidioc_s_fmt_vbi_cap))
- ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (likely(ops->vidioc_s_fmt_vbi_out))
- ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_s_fmt_vbi_out))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (likely(ops->vidioc_s_fmt_sliced_vbi_cap))
- ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (likely(ops->vidioc_s_fmt_sliced_vbi_out))
- ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
- fh, f);
-
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
break;
- case V4L2_BUF_TYPE_PRIVATE:
- /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (likely(ops->vidioc_s_fmt_type_private))
- ret = ops->vidioc_s_fmt_type_private(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (unlikely(!ops->vidioc_s_fmt_type_private))
break;
- }
- break;
+ return ops->vidioc_s_fmt_type_private(file, fh, arg);
}
- case VIDIOC_TRY_FMT:
- {
- struct v4l2_format *f = (struct v4l2_format *)arg;
-
- /* FIXME: Should be one dump per type */
- dbgarg(cmd, "type=%s\n", prt_names(f->type,
- v4l2_type_names));
- ret = -EINVAL;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_cap)
- ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
- if (!ret)
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ return -EINVAL;
+}
+
+static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!ops->vidioc_try_fmt_vid_cap))
break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_cap_mplane)
- ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
- fh, f);
- if (!ret)
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- CLEAR_AFTER_FIELD(f, fmt.win);
- if (likely(ops->vidioc_try_fmt_vid_overlay))
- ret = ops->vidioc_try_fmt_vid_overlay(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.pix);
- if (ops->vidioc_try_fmt_vid_out)
- ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
- if (!ret)
- v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!ops->vidioc_try_fmt_vid_out))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- CLEAR_AFTER_FIELD(f, fmt.pix_mp);
- if (ops->vidioc_try_fmt_vid_out_mplane)
- ret = ops->vidioc_try_fmt_vid_out_mplane(file,
- fh, f);
- if (!ret)
- v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_try_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- CLEAR_AFTER_FIELD(f, fmt.win);
- if (likely(ops->vidioc_try_fmt_vid_out_overlay))
- ret = ops->vidioc_try_fmt_vid_out_overlay(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (likely(ops->vidioc_try_fmt_vbi_cap))
- ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.vbi);
- if (likely(ops->vidioc_try_fmt_vbi_out))
- ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_try_fmt_vbi_out))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (likely(ops->vidioc_try_fmt_sliced_vbi_cap))
- ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- CLEAR_AFTER_FIELD(f, fmt.sliced);
- if (likely(ops->vidioc_try_fmt_sliced_vbi_out))
- ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
break;
- case V4L2_BUF_TYPE_PRIVATE:
- /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
- if (likely(ops->vidioc_try_fmt_type_private))
- ret = ops->vidioc_try_fmt_type_private(file,
- fh, f);
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (unlikely(!ops->vidioc_try_fmt_type_private))
break;
- }
- break;
+ return ops->vidioc_try_fmt_type_private(file, fh, arg);
}
- /* FIXME: Those buf reqs could be handled here,
- with some changes on videobuf to allow its header to be included at
- videodev2.h or being merged at videodev2.
- */
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *p = arg;
+ return -EINVAL;
+}
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
+static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
+}
- if (p->type < V4L2_BUF_TYPE_PRIVATE)
- CLEAR_AFTER_FIELD(p, memory);
+static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
+}
- ret = ops->vidioc_reqbufs(file, fh, p);
- dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
- p->count,
- prt_names(p->type, v4l2_type_names),
- prt_names(p->memory, v4l2_memory_names));
- break;
- }
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *p = arg;
+static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_tuner *p = arg;
+ int err;
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ err = ops->vidioc_g_tuner(file, fh, p);
+ if (!err)
+ p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+ return err;
+}
- ret = ops->vidioc_querybuf(file, fh, p);
- if (!ret)
- dbgbuf(cmd, vfd, p);
- break;
- }
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *p = arg;
+static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_tuner *p = arg;
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ return ops->vidioc_s_tuner(file, fh, p);
+}
- ret = ops->vidioc_qbuf(file, fh, p);
- if (!ret)
- dbgbuf(cmd, vfd, p);
- break;
- }
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *p = arg;
+static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_modulator *p = arg;
+ int err;
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
+ err = ops->vidioc_g_modulator(file, fh, p);
+ if (!err)
+ p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+ return err;
+}
- ret = ops->vidioc_dqbuf(file, fh, p);
- if (!ret)
- dbgbuf(cmd, vfd, p);
- break;
- }
- case VIDIOC_OVERLAY:
- {
- int *i = arg;
+static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency *p = arg;
- dbgarg(cmd, "value=%d\n", *i);
- ret = ops->vidioc_overlay(file, fh, *i);
- break;
- }
- case VIDIOC_G_FBUF:
- {
- struct v4l2_framebuffer *p = arg;
-
- ret = ops->vidioc_g_fbuf(file, fh, arg);
- if (!ret) {
- dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
- p->capability, p->flags,
- (unsigned long)p->base);
- v4l_print_pix_fmt(vfd, &p->fmt);
- }
- break;
- }
- case VIDIOC_S_FBUF:
- {
- struct v4l2_framebuffer *p = arg;
-
- dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
- p->capability, p->flags, (unsigned long)p->base);
- v4l_print_pix_fmt(vfd, &p->fmt);
- ret = ops->vidioc_s_fbuf(file, fh, arg);
- break;
- }
- case VIDIOC_STREAMON:
- {
- enum v4l2_buf_type i = *(int *)arg;
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ return ops->vidioc_g_frequency(file, fh, p);
+}
- dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
- ret = ops->vidioc_streamon(file, fh, i);
- break;
- }
- case VIDIOC_STREAMOFF:
- {
- enum v4l2_buf_type i = *(int *)arg;
+static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency *p = arg;
+ enum v4l2_tuner_type type;
- dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
- ret = ops->vidioc_streamoff(file, fh, i);
- break;
- }
- /* ---------- tv norms ---------- */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *p = arg;
- v4l2_std_id id = vfd->tvnorms, curr_id = 0;
- unsigned int index = p->index, i, j = 0;
- const char *descr = "";
-
- if (id == 0)
- break;
- ret = -EINVAL;
-
- /* Return norm array in a canonical way */
- for (i = 0; i <= index && id; i++) {
- /* last std value in the standards array is 0, so this
- while always ends there since (id & 0) == 0. */
- while ((id & standards[j].std) != standards[j].std)
- j++;
- curr_id = standards[j].std;
- descr = standards[j].descr;
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (p->type != type)
+ return -EINVAL;
+ return ops->vidioc_s_frequency(file, fh, p);
+}
+
+static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_standard *p = arg;
+ v4l2_std_id id = vfd->tvnorms, curr_id = 0;
+ unsigned int index = p->index, i, j = 0;
+ const char *descr = "";
+
+ /* Return norm array in a canonical way */
+ for (i = 0; i <= index && id; i++) {
+ /* last std value in the standards array is 0, so this
+ while always ends there since (id & 0) == 0. */
+ while ((id & standards[j].std) != standards[j].std)
j++;
- if (curr_id == 0)
- break;
- if (curr_id != V4L2_STD_PAL &&
- curr_id != V4L2_STD_SECAM &&
- curr_id != V4L2_STD_NTSC)
- id &= ~curr_id;
- }
- if (i <= index)
+ curr_id = standards[j].std;
+ descr = standards[j].descr;
+ j++;
+ if (curr_id == 0)
break;
+ if (curr_id != V4L2_STD_PAL &&
+ curr_id != V4L2_STD_SECAM &&
+ curr_id != V4L2_STD_NTSC)
+ id &= ~curr_id;
+ }
+ if (i <= index)
+ return -EINVAL;
- v4l2_video_std_construct(p, curr_id, descr);
+ v4l2_video_std_construct(p, curr_id, descr);
+ return 0;
+}
- dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, "
- "framelines=%d\n", p->index,
- (unsigned long long)p->id, p->name,
- p->frameperiod.numerator,
- p->frameperiod.denominator,
- p->framelines);
+static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *id = arg;
- ret = 0;
- break;
+ /* Calls the specific handler */
+ if (ops->vidioc_g_std)
+ return ops->vidioc_g_std(file, fh, arg);
+ if (vfd->current_norm) {
+ *id = vfd->current_norm;
+ return 0;
}
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
-
- /* Calls the specific handler */
- if (ops->vidioc_g_std)
- ret = ops->vidioc_g_std(file, fh, id);
- else if (vfd->current_norm) {
- ret = 0;
- *id = vfd->current_norm;
- }
+ return -ENOTTY;
+}
- if (likely(!ret))
- dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
- break;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg, norm;
+static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *id = arg, norm;
+ int ret;
- dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
+ norm = (*id) & vfd->tvnorms;
+ if (vfd->tvnorms && !norm) /* Check if std is supported */
+ return -EINVAL;
- ret = -EINVAL;
- norm = (*id) & vfd->tvnorms;
- if (vfd->tvnorms && !norm) /* Check if std is supported */
- break;
+ /* Calls the specific handler */
+ ret = ops->vidioc_s_std(file, fh, &norm);
- /* Calls the specific handler */
- ret = ops->vidioc_s_std(file, fh, &norm);
+ /* Updates standard information */
+ if (ret >= 0)
+ vfd->current_norm = norm;
+ return ret;
+}
- /* Updates standard information */
- if (ret >= 0)
- vfd->current_norm = norm;
- break;
- }
- case VIDIOC_QUERYSTD:
- {
- v4l2_std_id *p = arg;
+static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *p = arg;
+
+ /*
+ * If nothing detected, it should return all supported
+ * standard.
+ * Drivers just need to mask the std argument, in order
+ * to remove the standards that don't apply from the mask.
+ * This means that tuners, audio and video decoders can join
+ * their efforts to improve the standards detection.
+ */
+ *p = vfd->tvnorms;
+ return ops->vidioc_querystd(file, fh, arg);
+}
- /*
- * If nothing detected, it should return all supported
- * Drivers just need to mask the std argument, in order
- * to remove the standards that don't apply from the mask.
- * This means that tuners, audio and video decoders can join
- * their efforts to improve the standards detection
- */
- *p = vfd->tvnorms;
- ret = ops->vidioc_querystd(file, fh, arg);
- if (!ret)
- dbgarg(cmd, "detected std=%08Lx\n",
- (unsigned long long)*p);
- break;
- }
- /* ------ input switching ---------- */
- /* FIXME: Inputs can be handled inside videodev2 */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *p = arg;
+static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_hw_freq_seek *p = arg;
+ enum v4l2_tuner_type type;
- /*
- * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
- * CAP_STD here based on ioctl handler provided by the
- * driver. If the driver doesn't support these
- * for a specific input, it must override these flags.
- */
- if (ops->vidioc_s_std)
- p->capabilities |= V4L2_IN_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_IN_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
-
- ret = ops->vidioc_enum_input(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, type=%d, "
- "audioset=%d, "
- "tuner=%d, std=%08Lx, status=%d\n",
- p->index, p->name, p->type, p->audioset,
- p->tuner,
- (unsigned long long)p->std,
- p->status);
- break;
- }
- case VIDIOC_G_INPUT:
- {
- unsigned int *i = arg;
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (p->type != type)
+ return -EINVAL;
+ return ops->vidioc_s_hw_freq_seek(file, fh, p);
+}
- ret = ops->vidioc_g_input(file, fh, i);
- if (!ret)
- dbgarg(cmd, "value=%d\n", *i);
- break;
- }
- case VIDIOC_S_INPUT:
- {
- unsigned int *i = arg;
+static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_requestbuffers *p = arg;
+ int ret = check_fmt(ops, p->type);
- dbgarg(cmd, "value=%d\n", *i);
- ret = ops->vidioc_s_input(file, fh, *i);
- break;
- }
+ if (ret)
+ return ret;
- /* ------ output switching ---------- */
- case VIDIOC_ENUMOUTPUT:
- {
- struct v4l2_output *p = arg;
+ if (p->type < V4L2_BUF_TYPE_PRIVATE)
+ CLEAR_AFTER_FIELD(p, memory);
- /*
- * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
- * CAP_STD here based on ioctl handler provided by the
- * driver. If the driver doesn't support these
- * for a specific output, it must override these flags.
- */
- if (ops->vidioc_s_std)
- p->capabilities |= V4L2_OUT_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_OUT_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
-
- ret = ops->vidioc_enum_output(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, type=%d, "
- "audioset=0x%x, "
- "modulator=%d, std=0x%08Lx\n",
- p->index, p->name, p->type, p->audioset,
- p->modulator, (unsigned long long)p->std);
- break;
- }
- case VIDIOC_G_OUTPUT:
- {
- unsigned int *i = arg;
+ return ops->vidioc_reqbufs(file, fh, p);
+}
- ret = ops->vidioc_g_output(file, fh, i);
- if (!ret)
- dbgarg(cmd, "value=%d\n", *i);
- break;
- }
- case VIDIOC_S_OUTPUT:
- {
- unsigned int *i = arg;
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(ops, p->type);
- dbgarg(cmd, "value=%d\n", *i);
- ret = ops->vidioc_s_output(file, fh, *i);
- break;
- }
+ return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+}
- /* --- controls ---------------------------------------------- */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *p = arg;
-
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_queryctrl(vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_queryctrl(vfd->ctrl_handler, p);
- else if (ops->vidioc_queryctrl)
- ret = ops->vidioc_queryctrl(file, fh, p);
- else
- break;
- if (!ret)
- dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
- "step=%d, default=%d, flags=0x%08x\n",
- p->id, p->type, p->name,
- p->minimum, p->maximum,
- p->step, p->default_value, p->flags);
- else
- dbgarg(cmd, "id=0x%x\n", p->id);
- break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *p = arg;
-
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
- else if (ops->vidioc_g_ctrl)
- ret = ops->vidioc_g_ctrl(file, fh, p);
- else if (ops->vidioc_g_ext_ctrls) {
- struct v4l2_ext_controls ctrls;
- struct v4l2_ext_control ctrl;
-
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
- ctrls.count = 1;
- ctrls.controls = &ctrl;
- ctrl.id = p->id;
- ctrl.value = p->value;
- if (check_ext_ctrls(&ctrls, 1)) {
- ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
- if (ret == 0)
- p->value = ctrl.value;
- }
- } else
- break;
- if (!ret)
- dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
- else
- dbgarg(cmd, "id=0x%x\n", p->id);
- break;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *p = arg;
- struct v4l2_ext_controls ctrls;
- struct v4l2_ext_control ctrl;
-
- if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
- !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
- break;
+static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(ops, p->type);
- dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+}
- if (vfh && vfh->ctrl_handler) {
- ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
- break;
- }
- if (vfd->ctrl_handler) {
- ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
- break;
- }
- if (ops->vidioc_s_ctrl) {
- ret = ops->vidioc_s_ctrl(file, fh, p);
- break;
- }
- if (!ops->vidioc_s_ext_ctrls)
- break;
+static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(ops, p->type);
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
- ctrls.count = 1;
- ctrls.controls = &ctrl;
- ctrl.id = p->id;
- ctrl.value = p->value;
- if (check_ext_ctrls(&ctrls, 1))
- ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
- else
- ret = -EINVAL;
- break;
- }
- case VIDIOC_G_EXT_CTRLS:
- {
- struct v4l2_ext_controls *p = arg;
-
- p->error_idx = p->count;
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
- else if (ops->vidioc_g_ext_ctrls)
- ret = check_ext_ctrls(p, 0) ?
- ops->vidioc_g_ext_ctrls(file, fh, p) :
- -EINVAL;
- else
- break;
- v4l_print_ext_ctrls(cmd, vfd, p, !ret);
- break;
- }
- case VIDIOC_S_EXT_CTRLS:
- {
- struct v4l2_ext_controls *p = arg;
+ return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
+}
- p->error_idx = p->count;
- if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
- !ops->vidioc_s_ext_ctrls)
- break;
- v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
- else if (check_ext_ctrls(p, 0))
- ret = ops->vidioc_s_ext_ctrls(file, fh, p);
- else
- ret = -EINVAL;
- break;
- }
- case VIDIOC_TRY_EXT_CTRLS:
- {
- struct v4l2_ext_controls *p = arg;
+static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_create_buffers *create = arg;
+ int ret = check_fmt(ops, create->format.type);
- p->error_idx = p->count;
- if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
- !ops->vidioc_try_ext_ctrls)
- break;
- v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
- else if (check_ext_ctrls(p, 0))
- ret = ops->vidioc_try_ext_ctrls(file, fh, p);
- else
- ret = -EINVAL;
- break;
- }
- case VIDIOC_QUERYMENU:
- {
- struct v4l2_querymenu *p = arg;
-
- if (vfh && vfh->ctrl_handler)
- ret = v4l2_querymenu(vfh->ctrl_handler, p);
- else if (vfd->ctrl_handler)
- ret = v4l2_querymenu(vfd->ctrl_handler, p);
- else if (ops->vidioc_querymenu)
- ret = ops->vidioc_querymenu(file, fh, p);
- else
- break;
- if (!ret)
- dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
- p->id, p->index, p->name);
- else
- dbgarg(cmd, "id=0x%x, index=%d\n",
- p->id, p->index);
- break;
- }
- /* --- audio ---------------------------------------------- */
- case VIDIOC_ENUMAUDIO:
- {
- struct v4l2_audio *p = arg;
-
- ret = ops->vidioc_enumaudio(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
- "mode=0x%x\n", p->index, p->name,
- p->capability, p->mode);
- else
- dbgarg(cmd, "index=%d\n", p->index);
- break;
- }
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *p = arg;
-
- ret = ops->vidioc_g_audio(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
- "mode=0x%x\n", p->index,
- p->name, p->capability, p->mode);
- else
- dbgarg(cmd, "index=%d\n", p->index);
- break;
- }
- case VIDIOC_S_AUDIO:
- {
- struct v4l2_audio *p = arg;
-
- dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
- "mode=0x%x\n", p->index, p->name,
- p->capability, p->mode);
- ret = ops->vidioc_s_audio(file, fh, p);
- break;
- }
- case VIDIOC_ENUMAUDOUT:
- {
- struct v4l2_audioout *p = arg;
-
- dbgarg(cmd, "Enum for index=%d\n", p->index);
- ret = ops->vidioc_enumaudout(file, fh, p);
- if (!ret)
- dbgarg2("index=%d, name=%s, capability=%d, "
- "mode=%d\n", p->index, p->name,
- p->capability, p->mode);
- break;
- }
- case VIDIOC_G_AUDOUT:
- {
- struct v4l2_audioout *p = arg;
-
- ret = ops->vidioc_g_audout(file, fh, p);
- if (!ret)
- dbgarg2("index=%d, name=%s, capability=%d, "
- "mode=%d\n", p->index, p->name,
- p->capability, p->mode);
- break;
- }
- case VIDIOC_S_AUDOUT:
- {
- struct v4l2_audioout *p = arg;
+ return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
+}
- dbgarg(cmd, "index=%d, name=%s, capability=%d, "
- "mode=%d\n", p->index, p->name,
- p->capability, p->mode);
+static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *b = arg;
+ int ret = check_fmt(ops, b->type);
- ret = ops->vidioc_s_audout(file, fh, p);
- break;
- }
- case VIDIOC_G_MODULATOR:
- {
- struct v4l2_modulator *p = arg;
-
- ret = ops->vidioc_g_modulator(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, "
- "capability=%d, rangelow=%d,"
- " rangehigh=%d, txsubchans=%d\n",
- p->index, p->name, p->capability,
- p->rangelow, p->rangehigh,
- p->txsubchans);
- break;
- }
- case VIDIOC_S_MODULATOR:
- {
- struct v4l2_modulator *p = arg;
-
- dbgarg(cmd, "index=%d, name=%s, capability=%d, "
- "rangelow=%d, rangehigh=%d, txsubchans=%d\n",
- p->index, p->name, p->capability, p->rangelow,
- p->rangehigh, p->txsubchans);
- ret = ops->vidioc_s_modulator(file, fh, p);
- break;
- }
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop *p = arg;
+ return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+}
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_streamparm *p = arg;
+ v4l2_std_id std;
+ int ret = check_fmt(ops, p->type);
- if (ops->vidioc_g_crop) {
- ret = ops->vidioc_g_crop(file, fh, p);
- } else {
- /* simulate capture crop using selection api */
- struct v4l2_selection s = {
- .type = p->type,
- };
-
- /* crop means compose for output devices */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
- else
- s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
- ret = ops->vidioc_g_selection(file, fh, &s);
-
- /* copying results to old structure on success */
- if (!ret)
- p->c = s.r;
- }
+ if (ret)
+ return ret;
+ if (ops->vidioc_g_parm)
+ return ops->vidioc_g_parm(file, fh, p);
+ std = vfd->current_norm;
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+ p->parm.capture.readbuffers = 2;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
+ return ret;
+}
- if (!ret)
- dbgrect(vfd, "", &p->c);
- break;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *p = arg;
+static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_streamparm *p = arg;
+ int ret = check_fmt(ops, p->type);
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
- dbgrect(vfd, "", &p->c);
+ return ret ? ret : ops->vidioc_s_parm(file, fh, p);
+}
- if (ops->vidioc_s_crop) {
- ret = ops->vidioc_s_crop(file, fh, p);
- } else {
- /* simulate capture crop using selection api */
- struct v4l2_selection s = {
- .type = p->type,
- .r = p->c,
- };
-
- /* crop means compose for output devices */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
- else
- s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
- ret = ops->vidioc_s_selection(file, fh, &s);
- }
- break;
- }
- case VIDIOC_G_SELECTION:
- {
- struct v4l2_selection *p = arg;
+static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_queryctrl *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_queryctrl(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_queryctrl(vfd->ctrl_handler, p);
+ if (ops->vidioc_queryctrl)
+ return ops->vidioc_queryctrl(file, fh, p);
+ return -ENOTTY;
+}
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_querymenu *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_querymenu(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_querymenu(vfd->ctrl_handler, p);
+ if (ops->vidioc_querymenu)
+ return ops->vidioc_querymenu(file, fh, p);
+ return -ENOTTY;
+}
- ret = ops->vidioc_g_selection(file, fh, p);
- if (!ret)
- dbgrect(vfd, "", &p->r);
- break;
+static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_control *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_g_ctrl(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_g_ctrl(vfd->ctrl_handler, p);
+ if (ops->vidioc_g_ctrl)
+ return ops->vidioc_g_ctrl(file, fh, p);
+ if (ops->vidioc_g_ext_ctrls == NULL)
+ return -ENOTTY;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1)) {
+ int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+
+ if (ret == 0)
+ p->value = ctrl.value;
+ return ret;
}
- case VIDIOC_S_SELECTION:
- {
- struct v4l2_selection *p = arg;
+ return -EINVAL;
+}
+static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_control *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
+ if (ops->vidioc_s_ctrl)
+ return ops->vidioc_s_ctrl(file, fh, p);
+ if (ops->vidioc_s_ext_ctrls == NULL)
+ return -ENOTTY;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1))
+ return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+ return -EINVAL;
+}
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
- dbgrect(vfd, "", &p->r);
+static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ if (ops->vidioc_g_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
- ret = ops->vidioc_s_selection(file, fh, p);
- break;
- }
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *p = arg;
-
- /*FIXME: Should also show v4l2_fract pixelaspect */
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
- if (ops->vidioc_cropcap) {
- ret = ops->vidioc_cropcap(file, fh, p);
- } else {
- struct v4l2_selection s = { .type = p->type };
+static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+ if (ops->vidioc_s_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
- /* obtaining bounds */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
- else
- s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ if (ops->vidioc_try_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
- ret = ops->vidioc_g_selection(file, fh, &s);
- if (ret)
- break;
- p->bounds = s.r;
+static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_crop *p = arg;
+ struct v4l2_selection s = {
+ .type = p->type,
+ };
+ int ret;
+
+ if (ops->vidioc_g_crop)
+ return ops->vidioc_g_crop(file, fh, p);
+ /* simulate capture crop using selection api */
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+
+ /* copying results to old structure on success */
+ if (!ret)
+ p->c = s.r;
+ return ret;
+}
- /* obtaining defrect */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
- else
- s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_crop *p = arg;
+ struct v4l2_selection s = {
+ .type = p->type,
+ .r = p->c,
+ };
+
+ if (ops->vidioc_s_crop)
+ return ops->vidioc_s_crop(file, fh, p);
+ /* simulate capture crop using selection api */
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ return ops->vidioc_s_selection(file, fh, &s);
+}
- ret = ops->vidioc_g_selection(file, fh, &s);
- if (ret)
- break;
- p->defrect = s.r;
+static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_cropcap *p = arg;
+ struct v4l2_selection s = { .type = p->type };
+ int ret;
- /* setting trivial pixelaspect */
- p->pixelaspect.numerator = 1;
- p->pixelaspect.denominator = 1;
- }
+ if (ops->vidioc_cropcap)
+ return ops->vidioc_cropcap(file, fh, p);
- if (!ret) {
- dbgrect(vfd, "bounds ", &p->bounds);
- dbgrect(vfd, "defrect ", &p->defrect);
- }
- break;
- }
- case VIDIOC_G_JPEGCOMP:
- {
- struct v4l2_jpegcompression *p = arg;
-
- ret = ops->vidioc_g_jpegcomp(file, fh, p);
- if (!ret)
- dbgarg(cmd, "quality=%d, APPn=%d, "
- "APP_len=%d, COM_len=%d, "
- "jpeg_markers=%d\n",
- p->quality, p->APPn, p->APP_len,
- p->COM_len, p->jpeg_markers);
- break;
- }
- case VIDIOC_S_JPEGCOMP:
- {
- struct v4l2_jpegcompression *p = arg;
-
- dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, "
- "COM_len=%d, jpeg_markers=%d\n",
- p->quality, p->APPn, p->APP_len,
- p->COM_len, p->jpeg_markers);
- ret = ops->vidioc_s_jpegcomp(file, fh, p);
- break;
- }
- case VIDIOC_G_ENC_INDEX:
- {
- struct v4l2_enc_idx *p = arg;
-
- ret = ops->vidioc_g_enc_index(file, fh, p);
- if (!ret)
- dbgarg(cmd, "entries=%d, entries_cap=%d\n",
- p->entries, p->entries_cap);
- break;
- }
- case VIDIOC_ENCODER_CMD:
- {
- struct v4l2_encoder_cmd *p = arg;
+ /* obtaining bounds */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+ else
+ s.target = V4L2_SEL_TGT_CROP_BOUNDS;
- ret = ops->vidioc_encoder_cmd(file, fh, p);
- if (!ret)
- dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
- break;
- }
- case VIDIOC_TRY_ENCODER_CMD:
- {
- struct v4l2_encoder_cmd *p = arg;
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ return ret;
+ p->bounds = s.r;
- ret = ops->vidioc_try_encoder_cmd(file, fh, p);
- if (!ret)
- dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
- break;
- }
- case VIDIOC_DECODER_CMD:
- {
- struct v4l2_decoder_cmd *p = arg;
+ /* obtaining defrect */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+ else
+ s.target = V4L2_SEL_TGT_CROP_DEFAULT;
- ret = ops->vidioc_decoder_cmd(file, fh, p);
- if (!ret)
- dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
- break;
- }
- case VIDIOC_TRY_DECODER_CMD:
- {
- struct v4l2_decoder_cmd *p = arg;
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ return ret;
+ p->defrect = s.r;
- ret = ops->vidioc_try_decoder_cmd(file, fh, p);
- if (!ret)
- dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
- break;
- }
- case VIDIOC_G_PARM:
- {
- struct v4l2_streamparm *p = arg;
+ /* setting trivial pixelaspect */
+ p->pixelaspect.numerator = 1;
+ p->pixelaspect.denominator = 1;
+ return 0;
+}
- if (ops->vidioc_g_parm) {
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
- ret = ops->vidioc_g_parm(file, fh, p);
- } else {
- v4l2_std_id std = vfd->current_norm;
+static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ int ret;
+
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================= START STATUS =================\n",
+ vfd->v4l2_dev->name);
+ ret = ops->vidioc_log_status(file, fh);
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================== END STATUS ==================\n",
+ vfd->v4l2_dev->name);
+ return ret;
+}
- ret = -EINVAL;
- if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- break;
+static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct v4l2_dbg_register *p = arg;
- ret = 0;
- p->parm.capture.readbuffers = 2;
- if (ops->vidioc_g_std)
- ret = ops->vidioc_g_std(file, fh, &std);
- if (ret == 0)
- v4l2_video_std_frame_period(std,
- &p->parm.capture.timeperframe);
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ops->vidioc_g_register(file, fh, p);
+#else
+ return -ENOTTY;
+#endif
+}
- dbgarg(cmd, "type=%d\n", p->type);
- break;
- }
- case VIDIOC_S_PARM:
- {
- struct v4l2_streamparm *p = arg;
+static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct v4l2_dbg_register *p = arg;
- ret = check_fmt(ops, p->type);
- if (ret)
- break;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ops->vidioc_s_register(file, fh, p);
+#else
+ return -ENOTTY;
+#endif
+}
- dbgarg(cmd, "type=%d\n", p->type);
- ret = ops->vidioc_s_parm(file, fh, p);
- break;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *p = arg;
+static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_dbg_chip_ident *p = arg;
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- ret = ops->vidioc_g_tuner(file, fh, p);
- if (!ret)
- dbgarg(cmd, "index=%d, name=%s, type=%d, "
- "capability=0x%x, rangelow=%d, "
- "rangehigh=%d, signal=%d, afc=%d, "
- "rxsubchans=0x%x, audmode=%d\n",
- p->index, p->name, p->type,
- p->capability, p->rangelow,
- p->rangehigh, p->signal, p->afc,
- p->rxsubchans, p->audmode);
- break;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *p = arg;
+ p->ident = V4L2_IDENT_NONE;
+ p->revision = 0;
+ return ops->vidioc_g_chip_ident(file, fh, p);
+}
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- dbgarg(cmd, "index=%d, name=%s, type=%d, "
- "capability=0x%x, rangelow=%d, "
- "rangehigh=%d, signal=%d, afc=%d, "
- "rxsubchans=0x%x, audmode=%d\n",
- p->index, p->name, p->type,
- p->capability, p->rangelow,
- p->rangehigh, p->signal, p->afc,
- p->rxsubchans, p->audmode);
- ret = ops->vidioc_s_tuner(file, fh, p);
- break;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *p = arg;
+static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
+}
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- ret = ops->vidioc_g_frequency(file, fh, p);
- if (!ret)
- dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
- p->tuner, p->type, p->frequency);
- break;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *p = arg;
- enum v4l2_tuner_type type;
+static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_subscribe_event(fh, arg);
+}
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
- p->tuner, p->type, p->frequency);
- if (p->type != type)
- ret = -EINVAL;
- else
- ret = ops->vidioc_s_frequency(file, fh, p);
- break;
- }
- case VIDIOC_G_SLICED_VBI_CAP:
- {
- struct v4l2_sliced_vbi_cap *p = arg;
+static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_unsubscribe_event(fh, arg);
+}
- /* Clear up to type, everything after type is zerod already */
- memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_sliced_vbi_cap *p = arg;
- dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
- ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p);
- if (!ret)
- dbgarg2("service_set=%d\n", p->service_set);
- break;
- }
- case VIDIOC_LOG_STATUS:
- {
- if (vfd->v4l2_dev)
- pr_info("%s: ================= START STATUS =================\n",
- vfd->v4l2_dev->name);
- ret = ops->vidioc_log_status(file, fh);
- if (vfd->v4l2_dev)
- pr_info("%s: ================== END STATUS ==================\n",
- vfd->v4l2_dev->name);
- break;
- }
- case VIDIOC_DBG_G_REGISTER:
- {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register *p = arg;
+ /* Clear up to type, everything after type is zeroed already */
+ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
- if (!capable(CAP_SYS_ADMIN))
- ret = -EPERM;
- else
- ret = ops->vidioc_g_register(file, fh, p);
-#endif
- break;
- }
- case VIDIOC_DBG_S_REGISTER:
- {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register *p = arg;
+ return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+}
- if (!capable(CAP_SYS_ADMIN))
- ret = -EPERM;
- else
- ret = ops->vidioc_s_register(file, fh, p);
-#endif
- break;
- }
- case VIDIOC_DBG_G_CHIP_IDENT:
- {
- struct v4l2_dbg_chip_ident *p = arg;
-
- p->ident = V4L2_IDENT_NONE;
- p->revision = 0;
- ret = ops->vidioc_g_chip_ident(file, fh, p);
- if (!ret)
- dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
- break;
- }
- case VIDIOC_S_HW_FREQ_SEEK:
- {
- struct v4l2_hw_freq_seek *p = arg;
- enum v4l2_tuner_type type;
+static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency_band *p = arg;
+ enum v4l2_tuner_type type;
+ int err;
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- dbgarg(cmd,
- "tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
- p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
- if (p->type != type)
- ret = -EINVAL;
- else
- ret = ops->vidioc_s_hw_freq_seek(file, fh, p);
- break;
- }
- case VIDIOC_ENUM_FRAMESIZES:
- {
- struct v4l2_frmsizeenum *p = arg;
- ret = ops->vidioc_enum_framesizes(file, fh, p);
- dbgarg(cmd,
- "index=%d, pixelformat=%c%c%c%c, type=%d ",
- p->index,
- (p->pixel_format & 0xff),
- (p->pixel_format >> 8) & 0xff,
- (p->pixel_format >> 16) & 0xff,
- (p->pixel_format >> 24) & 0xff,
- p->type);
- switch (p->type) {
- case V4L2_FRMSIZE_TYPE_DISCRETE:
- dbgarg3("width = %d, height=%d\n",
- p->discrete.width, p->discrete.height);
- break;
- case V4L2_FRMSIZE_TYPE_STEPWISE:
- dbgarg3("min %dx%d, max %dx%d, step %dx%d\n",
- p->stepwise.min_width, p->stepwise.min_height,
- p->stepwise.step_width, p->stepwise.step_height,
- p->stepwise.max_width, p->stepwise.max_height);
- break;
- case V4L2_FRMSIZE_TYPE_CONTINUOUS:
- dbgarg3("continuous\n");
- break;
- default:
- dbgarg3("- Unknown type!\n");
- }
-
- break;
- }
- case VIDIOC_ENUM_FRAMEINTERVALS:
- {
- struct v4l2_frmivalenum *p = arg;
-
- ret = ops->vidioc_enum_frameintervals(file, fh, p);
- dbgarg(cmd,
- "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
- p->index, p->pixel_format,
- p->width, p->height, p->type);
- switch (p->type) {
- case V4L2_FRMIVAL_TYPE_DISCRETE:
- dbgarg2("fps=%d/%d\n",
- p->discrete.numerator,
- p->discrete.denominator);
- break;
- case V4L2_FRMIVAL_TYPE_STEPWISE:
- dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n",
- p->stepwise.min.numerator,
- p->stepwise.min.denominator,
- p->stepwise.max.numerator,
- p->stepwise.max.denominator,
- p->stepwise.step.numerator,
- p->stepwise.step.denominator);
- break;
- case V4L2_FRMIVAL_TYPE_CONTINUOUS:
- dbgarg2("continuous\n");
- break;
- default:
- dbgarg2("- Unknown type!\n");
- }
- break;
+ if (type != p->type)
+ return -EINVAL;
+ if (ops->vidioc_enum_freq_bands)
+ return ops->vidioc_enum_freq_bands(file, fh, p);
+ if (ops->vidioc_g_tuner) {
+ struct v4l2_tuner t = {
+ .index = p->tuner,
+ .type = type,
+ };
+
+ err = ops->vidioc_g_tuner(file, fh, &t);
+ if (err)
+ return err;
+ p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+ p->rangelow = t.rangelow;
+ p->rangehigh = t.rangehigh;
+ p->modulation = (type == V4L2_TUNER_RADIO) ?
+ V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+ return 0;
}
- case VIDIOC_ENUM_DV_PRESETS:
- {
- struct v4l2_dv_enum_preset *p = arg;
-
- ret = ops->vidioc_enum_dv_presets(file, fh, p);
- if (!ret)
- dbgarg(cmd,
- "index=%d, preset=%d, name=%s, width=%d,"
- " height=%d ",
- p->index, p->preset, p->name, p->width,
- p->height);
- break;
+ if (ops->vidioc_g_modulator) {
+ struct v4l2_modulator m = {
+ .index = p->tuner,
+ };
+
+ if (type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ err = ops->vidioc_g_modulator(file, fh, &m);
+ if (err)
+ return err;
+ p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+ p->rangelow = m.rangelow;
+ p->rangehigh = m.rangehigh;
+ p->modulation = (type == V4L2_TUNER_RADIO) ?
+ V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+ return 0;
}
- case VIDIOC_S_DV_PRESET:
- {
- struct v4l2_dv_preset *p = arg;
+ return -ENOTTY;
+}
- dbgarg(cmd, "preset=%d\n", p->preset);
- ret = ops->vidioc_s_dv_preset(file, fh, p);
- break;
- }
- case VIDIOC_G_DV_PRESET:
- {
- struct v4l2_dv_preset *p = arg;
+struct v4l2_ioctl_info {
+ unsigned int ioctl;
+ u32 flags;
+ const char * const name;
+ union {
+ u32 offset;
+ int (*func)(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *p);
+ } u;
+ void (*debug)(const void *arg, bool write_only);
+};
- ret = ops->vidioc_g_dv_preset(file, fh, p);
- if (!ret)
- dbgarg(cmd, "preset=%d\n", p->preset);
- break;
+/* This control needs a priority check */
+#define INFO_FL_PRIO (1 << 0)
+/* This control can be valid if the filehandle passes a control handler. */
+#define INFO_FL_CTRL (1 << 1)
+/* This is a standard ioctl, no need for special code */
+#define INFO_FL_STD (1 << 2)
+/* This is ioctl has its own function */
+#define INFO_FL_FUNC (1 << 3)
+/* Queuing ioctl */
+#define INFO_FL_QUEUE (1 << 4)
+/* Zero struct from after the field to the end */
+#define INFO_FL_CLEAR(v4l2_struct, field) \
+ ((offsetof(struct v4l2_struct, field) + \
+ sizeof(((struct v4l2_struct *)0)->field)) << 16)
+#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
+
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
+ [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags | INFO_FL_STD, \
+ .name = #_ioctl, \
+ .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
+ .debug = _debug, \
+ }
+
+#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
+ [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags | INFO_FL_FUNC, \
+ .name = #_ioctl, \
+ .u.func = _func, \
+ .debug = _debug, \
}
- case VIDIOC_QUERY_DV_PRESET:
- {
- struct v4l2_dv_preset *p = arg;
- ret = ops->vidioc_query_dv_preset(file, fh, p);
- if (!ret)
- dbgarg(cmd, "preset=%d\n", p->preset);
- break;
- }
- case VIDIOC_S_DV_TIMINGS:
- {
- struct v4l2_dv_timings *p = arg;
-
- dbgtimings(vfd, p);
- switch (p->type) {
- case V4L2_DV_BT_656_1120:
- ret = ops->vidioc_s_dv_timings(file, fh, p);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- break;
- }
- case VIDIOC_G_DV_TIMINGS:
- {
- struct v4l2_dv_timings *p = arg;
+static struct v4l2_ioctl_info v4l2_ioctls[] = {
+ IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
+ IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
+ IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+ IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
+ IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
+ IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
+ IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
+ IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
+ IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
+ IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
+ IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
+ IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
+ IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
+ IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
+ IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
+ IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
+ IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
+ IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
+ IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
+ IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
+ IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
+ IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
+ IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
+ IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
+ IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
+ IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
+ IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
+ IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
+ IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
+ IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
+ IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
+ IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
+ IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
+ IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
- ret = ops->vidioc_g_dv_timings(file, fh, p);
- if (!ret)
- dbgtimings(vfd, p);
- break;
- }
- case VIDIOC_ENUM_DV_TIMINGS:
- {
- struct v4l2_enum_dv_timings *p = arg;
+bool v4l2_is_known_ioctl(unsigned int cmd)
+{
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+ return false;
+ return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+}
- if (!ops->vidioc_enum_dv_timings)
- break;
+struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
+{
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+ return vdev->lock;
+ if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
+ return NULL;
+ if (vdev->queue && vdev->queue->lock &&
+ (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
+ return vdev->queue->lock;
+ return vdev->lock;
+}
- ret = ops->vidioc_enum_dv_timings(file, fh, p);
- if (!ret) {
- dbgarg(cmd, "index=%d: ", p->index);
- dbgtimings(vfd, &p->timings);
- }
- break;
- }
- case VIDIOC_QUERY_DV_TIMINGS:
- {
- struct v4l2_dv_timings *p = arg;
+/* Common ioctl debug function. This function can be used by
+ external ioctl messages as well as internal V4L ioctl */
+void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
+{
+ const char *dir, *type;
- if (!ops->vidioc_query_dv_timings)
- break;
+ if (prefix)
+ printk(KERN_DEBUG "%s: ", prefix);
- ret = ops->vidioc_query_dv_timings(file, fh, p);
- if (!ret)
- dbgtimings(vfd, p);
+ switch (_IOC_TYPE(cmd)) {
+ case 'd':
+ type = "v4l2_int";
break;
- }
- case VIDIOC_DV_TIMINGS_CAP:
- {
- struct v4l2_dv_timings_cap *p = arg;
-
- if (!ops->vidioc_dv_timings_cap)
- break;
-
- ret = ops->vidioc_dv_timings_cap(file, fh, p);
- if (ret)
- break;
- switch (p->type) {
- case V4L2_DV_BT_656_1120:
- dbgarg(cmd,
- "type=%d, width=%u-%u, height=%u-%u, "
- "pixelclock=%llu-%llu, standards=%x, capabilities=%x ",
- p->type,
- p->bt.min_width, p->bt.max_width,
- p->bt.min_height, p->bt.max_height,
- p->bt.min_pixelclock, p->bt.max_pixelclock,
- p->bt.standards, p->bt.capabilities);
- break;
- default:
- dbgarg(cmd, "unknown type ");
+ case 'V':
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+ type = "v4l2";
break;
}
+ pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
+ return;
+ default:
+ type = "unknown";
break;
}
- case VIDIOC_DQEVENT:
- {
- struct v4l2_event *ev = arg;
- ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK);
- if (ret < 0) {
- dbgarg(cmd, "no pending events?");
- break;
- }
- dbgarg(cmd,
- "pending=%d, type=0x%8.8x, sequence=%d, "
- "timestamp=%lu.%9.9lu ",
- ev->pending, ev->type, ev->sequence,
- ev->timestamp.tv_sec, ev->timestamp.tv_nsec);
- break;
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE: dir = "--"; break;
+ case _IOC_READ: dir = "r-"; break;
+ case _IOC_WRITE: dir = "-w"; break;
+ case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+ default: dir = "*ERR*"; break;
}
- case VIDIOC_SUBSCRIBE_EVENT:
- {
- struct v4l2_event_subscription *sub = arg;
+ pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
+ type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+}
+EXPORT_SYMBOL(v4l_printk_ioctl);
- ret = ops->vidioc_subscribe_event(fh, sub);
- if (ret < 0) {
- dbgarg(cmd, "failed, ret=%ld", ret);
- break;
- }
- dbgarg(cmd, "type=0x%8.8x", sub->type);
- break;
- }
- case VIDIOC_UNSUBSCRIBE_EVENT:
- {
- struct v4l2_event_subscription *sub = arg;
+static long __video_do_ioctl(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+ bool write_only = false;
+ struct v4l2_ioctl_info default_info;
+ const struct v4l2_ioctl_info *info;
+ void *fh = file->private_data;
+ struct v4l2_fh *vfh = NULL;
+ int use_fh_prio = 0;
+ int debug = vfd->debug;
+ long ret = -ENOTTY;
- ret = ops->vidioc_unsubscribe_event(fh, sub);
- if (ret < 0) {
- dbgarg(cmd, "failed, ret=%ld", ret);
- break;
- }
- dbgarg(cmd, "type=0x%8.8x", sub->type);
- break;
+ if (ops == NULL) {
+ pr_warn("%s: has no ioctl_ops.\n",
+ video_device_node_name(vfd));
+ return ret;
}
- case VIDIOC_CREATE_BUFS:
- {
- struct v4l2_create_buffers *create = arg;
- ret = check_fmt(ops, create->format.type);
- if (ret)
- break;
-
- ret = ops->vidioc_create_bufs(file, fh, create);
-
- dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
- break;
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ vfh = file->private_data;
+ use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
}
- case VIDIOC_PREPARE_BUF:
- {
- struct v4l2_buffer *b = arg;
-
- ret = check_fmt(ops, b->type);
- if (ret)
- break;
- ret = ops->vidioc_prepare_buf(file, fh, b);
+ if (v4l2_is_known_ioctl(cmd)) {
+ info = &v4l2_ioctls[_IOC_NR(cmd)];
- dbgarg(cmd, "index=%d", b->index);
- break;
- }
- default:
- if (!ops->vidioc_default)
- break;
- ret = ops->vidioc_default(file, fh, use_fh_prio ?
- v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
- cmd, arg);
- break;
- } /* switch */
+ if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
+ !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+ goto done;
- if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
- if (ret < 0) {
- v4l_print_ioctl(vfd->name, cmd);
- printk(KERN_CONT " error %ld\n", ret);
+ if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
+ ret = v4l2_prio_check(vfd->prio, vfh->prio);
+ if (ret)
+ goto done;
+ }
+ } else {
+ default_info.ioctl = cmd;
+ default_info.flags = 0;
+ default_info.debug = v4l_print_default;
+ info = &default_info;
+ }
+
+ write_only = _IOC_DIR(cmd) == _IOC_WRITE;
+ if (write_only && debug > V4L2_DEBUG_IOCTL) {
+ v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+ pr_cont(": ");
+ info->debug(arg, write_only);
+ }
+ if (info->flags & INFO_FL_STD) {
+ typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
+ const void *p = vfd->ioctl_ops;
+ const vidioc_op *vidioc = p + info->u.offset;
+
+ ret = (*vidioc)(file, fh, arg);
+ } else if (info->flags & INFO_FL_FUNC) {
+ ret = info->u.func(ops, file, fh, arg);
+ } else if (!ops->vidioc_default) {
+ ret = -ENOTTY;
+ } else {
+ ret = ops->vidioc_default(file, fh,
+ use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+ cmd, arg);
+ }
+
+done:
+ if (debug) {
+ if (write_only && debug > V4L2_DEBUG_IOCTL) {
+ if (ret < 0)
+ printk(KERN_DEBUG "%s: error %ld\n",
+ video_device_node_name(vfd), ret);
+ return ret;
+ }
+ v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+ if (ret < 0)
+ pr_cont(": error %ld\n", ret);
+ else if (debug == V4L2_DEBUG_IOCTL)
+ pr_cont("\n");
+ else if (_IOC_DIR(cmd) == _IOC_NONE)
+ info->debug(arg, write_only);
+ else {
+ pr_cont(": ");
+ info->debug(arg, write_only);
}
}
return ret;
}
-/* In some cases, only a few fields are used as input, i.e. when the app sets
- * "index" and then the driver fills in the rest of the structure for the thing
- * with that index. We only need to copy up the first non-input field. */
-static unsigned long cmd_input_size(unsigned int cmd)
-{
- /* Size of structure up to and including 'field' */
-#define CMDINSIZE(cmd, type, field) \
- case VIDIOC_##cmd: \
- return offsetof(struct v4l2_##type, field) + \
- sizeof(((struct v4l2_##type *)0)->field);
-
- switch (cmd) {
- CMDINSIZE(ENUM_FMT, fmtdesc, type);
- CMDINSIZE(G_FMT, format, type);
- CMDINSIZE(QUERYBUF, buffer, length);
- CMDINSIZE(G_PARM, streamparm, type);
- CMDINSIZE(ENUMSTD, standard, index);
- CMDINSIZE(ENUMINPUT, input, index);
- CMDINSIZE(G_CTRL, control, id);
- CMDINSIZE(G_TUNER, tuner, index);
- CMDINSIZE(QUERYCTRL, queryctrl, id);
- CMDINSIZE(QUERYMENU, querymenu, index);
- CMDINSIZE(ENUMOUTPUT, output, index);
- CMDINSIZE(G_MODULATOR, modulator, index);
- CMDINSIZE(G_FREQUENCY, frequency, tuner);
- CMDINSIZE(CROPCAP, cropcap, type);
- CMDINSIZE(G_CROP, crop, type);
- CMDINSIZE(ENUMAUDIO, audio, index);
- CMDINSIZE(ENUMAUDOUT, audioout, index);
- CMDINSIZE(ENCODER_CMD, encoder_cmd, flags);
- CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags);
- CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type);
- CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
- CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
- default:
- return _IOC_SIZE(cmd);
- }
-}
-
static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
void * __user *user_ptr, void ***kernel_ptr)
{
@@ -2219,7 +2235,20 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
err = -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
- unsigned long n = cmd_input_size(cmd);
+ unsigned int n = _IOC_SIZE(cmd);
+
+ /*
+ * In some cases, only a few fields are used as input,
+ * i.e. when the app sets "index" and then the driver
+ * fills in the rest of the structure for the thing
+ * with that index. We only need to copy up the first
+ * non-input field.
+ */
+ if (v4l2_is_known_ioctl(cmd)) {
+ u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
+ if (flags & INFO_FL_CLEAR_MASK)
+ n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+ }
if (copy_from_user(parg, (void __user *)arg, n))
goto out;
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index 975d0fa938c6..97b48318aee1 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -19,6 +19,9 @@
#include <media/videobuf2-core.h>
#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
@@ -407,11 +410,24 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct poll_table_struct *wait)
{
+ struct video_device *vfd = video_devdata(file);
+ unsigned long req_events = poll_requested_events(wait);
struct vb2_queue *src_q, *dst_q;
struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
unsigned int rc = 0;
unsigned long flags;
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ rc = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->wait, wait);
+ if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
+ return rc;
+ }
+
src_q = v4l2_m2m_get_src_vq(m2m_ctx);
dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
@@ -422,7 +438,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
*/
if ((!src_q->streaming || list_empty(&src_q->queued_list))
&& (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
- rc = POLLERR;
+ rc |= POLLERR;
goto end;
}
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index db6e859b93d4..9182f81deb5b 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
- sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+ sel.target = V4L2_SEL_TGT_CROP;
rval = v4l2_subdev_call(
sd, pad, get_selection, subdev_fh, &sel);
@@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
- sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+ sel.target = V4L2_SEL_TGT_CROP;
sel.r = crop->rect;
rval = v4l2_subdev_call(
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 308e150a39bc..eb404c2ce270 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -963,7 +963,7 @@ static int viacam_do_try_fmt(struct via_camera *cam,
upix->pixelformat = f->pixelformat;
viacam_fmt_pre(upix, spix);
- v4l2_fill_mbus_format(&mbus_fmt, upix, f->mbus_code);
+ 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);
viacam_fmt_post(upix, spix);
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index ffdf59cfe405..bf7a326b1cdc 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -359,11 +359,6 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
break;
}
- if (vb->input != UNSET) {
- b->flags |= V4L2_BUF_FLAG_INPUT;
- b->input = vb->input;
- }
-
b->field = vb->field;
b->timestamp = vb->ts;
b->bytesused = vb->size;
@@ -402,7 +397,6 @@ int __videobuf_mmap_setup(struct videobuf_queue *q,
break;
q->bufs[i]->i = i;
- q->bufs[i]->input = UNSET;
q->bufs[i]->memory = memory;
q->bufs[i]->bsize = bsize;
switch (memory) {
@@ -566,16 +560,6 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
goto done;
}
- if (b->flags & V4L2_BUF_FLAG_INPUT) {
- if (b->input >= q->inputs) {
- dprintk(1, "qbuf: wrong input.\n");
- goto done;
- }
- buf->input = b->input;
- } else {
- buf->input = UNSET;
- }
-
switch (b->memory) {
case V4L2_MEMORY_MMAP:
if (0 == buf->baddr) {
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index b6b5cc1a43cb..3a43ba0959bf 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -40,7 +40,7 @@ struct videobuf_dma_contig_memory {
static int __videobuf_dc_alloc(struct device *dev,
struct videobuf_dma_contig_memory *mem,
- unsigned long size, unsigned long flags)
+ unsigned long size, gfp_t flags)
{
mem->size = size;
if (mem->cached) {
@@ -56,7 +56,7 @@ static int __videobuf_dc_alloc(struct device *dev,
dev_err(dev, "dma_map_single failed\n");
free_pages_exact(mem->vaddr, mem->size);
- mem->vaddr = 0;
+ mem->vaddr = NULL;
return err;
}
}
@@ -359,32 +359,43 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
size = vma->vm_end - vma->vm_start;
size = (size < mem->size) ? size : mem->size;
- if (!mem->cached)
+ if (!mem->cached) {
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- pos = (unsigned long)mem->vaddr;
-
- while (size > 0) {
- page = virt_to_page((void *)pos);
- if (NULL == page) {
- dev_err(q->dev, "mmap: virt_to_page failed\n");
- __videobuf_dc_free(q->dev, mem);
- goto error;
- }
- retval = vm_insert_page(vma, start, page);
+ retval = remap_pfn_range(vma, vma->vm_start,
+ mem->dma_handle >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
if (retval) {
- dev_err(q->dev, "mmap: insert failed with error %d\n",
- retval);
- __videobuf_dc_free(q->dev, mem);
+ dev_err(q->dev, "mmap: remap failed with error %d. ",
+ retval);
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
goto error;
}
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
+ } else {
+ pos = (unsigned long)mem->vaddr;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
+ while (size > 0) {
+ page = virt_to_page((void *)pos);
+ if (NULL == page) {
+ dev_err(q->dev, "mmap: virt_to_page failed\n");
+ __videobuf_dc_free(q->dev, mem);
+ goto error;
+ }
+ retval = vm_insert_page(vma, start, page);
+ if (retval) {
+ dev_err(q->dev, "mmap: insert failed with error %d\n",
+ retval);
+ __videobuf_dc_free(q->dev, mem);
+ goto error;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
}
vma->vm_ops = &videobuf_vm_ops;
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 9d4e9edbd2e7..268c7dd4f823 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -336,9 +336,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
struct vb2_queue *q = vb->vb2_queue;
int ret;
- /* Copy back data such as timestamp, flags, input, etc. */
+ /* Copy back data such as timestamp, flags, etc. */
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
- b->input = vb->v4l2_buf.input;
+ b->reserved2 = vb->v4l2_buf.reserved2;
b->reserved = vb->v4l2_buf.reserved;
if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
@@ -454,7 +454,50 @@ static int __verify_mmap_ops(struct vb2_queue *q)
}
/**
- * vb2_reqbufs() - Initiate streaming
+ * __verify_memory_type() - Check whether the memory type and buffer type
+ * passed to a buffer operation are compatible with the queue.
+ */
+static int __verify_memory_type(struct vb2_queue *q,
+ enum v4l2_memory memory, enum v4l2_buf_type type)
+{
+ if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "reqbufs: unsupported memory type\n");
+ return -EINVAL;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "reqbufs: requested type is incorrect\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Place the busy tests at the end: -EBUSY can be ignored when
+ * create_bufs is called with count == 0, but count == 0 should still
+ * do the memory and type validation.
+ */
+ if (q->fileio) {
+ dprintk(1, "reqbufs: file io in progress\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/**
+ * __reqbufs() - Initiate streaming
* @q: videobuf2 queue
* @req: struct passed from userspace to vidioc_reqbufs handler in driver
*
@@ -476,46 +519,16 @@ static int __verify_mmap_ops(struct vb2_queue *q)
* The return values from this function are intended to be directly returned
* from vidioc_reqbufs handler in driver.
*/
-int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int num_buffers, allocated_buffers, num_planes = 0;
- int ret = 0;
-
- if (q->fileio) {
- dprintk(1, "reqbufs: file io in progress\n");
- return -EBUSY;
- }
-
- if (req->memory != V4L2_MEMORY_MMAP
- && req->memory != V4L2_MEMORY_USERPTR) {
- dprintk(1, "reqbufs: unsupported memory type\n");
- return -EINVAL;
- }
-
- if (req->type != q->type) {
- dprintk(1, "reqbufs: requested type is incorrect\n");
- return -EINVAL;
- }
+ int ret;
if (q->streaming) {
dprintk(1, "reqbufs: streaming active\n");
return -EBUSY;
}
- /*
- * Make sure all the required memory ops for given memory type
- * are available.
- */
- if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
- dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
- return -EINVAL;
- }
-
- if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
- dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
- return -EINVAL;
- }
-
if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
/*
* We already have buffers allocated, so first check if they
@@ -595,10 +608,23 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return 0;
}
+
+/**
+ * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
+ * type values.
+ * @q: videobuf2 queue
+ * @req: struct passed from userspace to vidioc_reqbufs handler in driver
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ int ret = __verify_memory_type(q, req->memory, req->type);
+
+ return ret ? ret : __reqbufs(q, req);
+}
EXPORT_SYMBOL_GPL(vb2_reqbufs);
/**
- * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
+ * __create_bufs() - Allocate buffers and any required auxiliary structs
* @q: videobuf2 queue
* @create: creation parameters, passed from userspace to vidioc_create_bufs
* handler in driver
@@ -612,40 +638,10 @@ EXPORT_SYMBOL_GPL(vb2_reqbufs);
* The return values from this function are intended to be directly returned
* from vidioc_create_bufs handler in driver.
*/
-int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
{
unsigned int num_planes = 0, num_buffers, allocated_buffers;
- int ret = 0;
-
- if (q->fileio) {
- dprintk(1, "%s(): file io in progress\n", __func__);
- return -EBUSY;
- }
-
- if (create->memory != V4L2_MEMORY_MMAP
- && create->memory != V4L2_MEMORY_USERPTR) {
- dprintk(1, "%s(): unsupported memory type\n", __func__);
- return -EINVAL;
- }
-
- if (create->format.type != q->type) {
- dprintk(1, "%s(): requested type is incorrect\n", __func__);
- return -EINVAL;
- }
-
- /*
- * Make sure all the required memory ops for given memory type
- * are available.
- */
- if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
- dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
- return -EINVAL;
- }
-
- if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
- dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
- return -EINVAL;
- }
+ int ret;
if (q->num_buffers == VIDEO_MAX_FRAME) {
dprintk(1, "%s(): maximum number of buffers already allocated\n",
@@ -653,8 +649,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
return -ENOBUFS;
}
- create->index = q->num_buffers;
-
if (!q->num_buffers) {
memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
@@ -675,9 +669,9 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
/* Finally, allocate buffers and video memory */
ret = __vb2_queue_alloc(q, create->memory, num_buffers,
num_planes);
- if (ret < 0) {
- dprintk(1, "Memory allocation failed with error: %d\n", ret);
- return ret;
+ if (ret == 0) {
+ dprintk(1, "Memory allocation failed\n");
+ return -ENOMEM;
}
allocated_buffers = ret;
@@ -708,7 +702,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
if (ret < 0) {
__vb2_queue_free(q, allocated_buffers);
- return ret;
+ return -ENOMEM;
}
/*
@@ -719,6 +713,23 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
return 0;
}
+
+/**
+ * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
+ * memory and type values.
+ * @q: videobuf2 queue
+ * @create: creation parameters, passed from userspace to vidioc_create_bufs
+ * handler in driver
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ int ret = __verify_memory_type(q, create->memory, create->format.type);
+
+ create->index = q->num_buffers;
+ if (create->count == 0)
+ return ret != -EBUSY ? ret : 0;
+ return ret ? ret : __create_bufs(q, create);
+}
EXPORT_SYMBOL_GPL(vb2_create_bufs);
/**
@@ -860,7 +871,6 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
vb->v4l2_buf.field = b->field;
vb->v4l2_buf.timestamp = b->timestamp;
- vb->v4l2_buf.input = b->input;
vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
return 0;
@@ -2115,6 +2125,263 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
}
EXPORT_SYMBOL_GPL(vb2_write);
+
+/*
+ * The following functions are not part of the vb2 core API, but are helper
+ * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
+ * and struct vb2_ops.
+ * They contain boilerplate code that most if not all drivers have to do
+ * and so they simplify the driver code.
+ */
+
+/* The queue is busy if there is a owner and you are not that owner. */
+static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
+{
+ return vdev->queue->owner && vdev->queue->owner != file->private_data;
+}
+
+/* vb2 ioctl helpers */
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = __verify_memory_type(vdev->queue, p->memory, p->type);
+
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ res = __reqbufs(vdev->queue, p);
+ /* If count == 0, then the owner has released all buffers and he
+ is no longer owner of the queue. Otherwise we have a new owner. */
+ if (res == 0)
+ vdev->queue->owner = p->count ? file->private_data : NULL;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
+
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
+
+ p->index = vdev->queue->num_buffers;
+ /* If count == 0, then just check if memory and type are valid.
+ Any -EBUSY result from __verify_memory_type can be mapped to 0. */
+ if (p->count == 0)
+ return res != -EBUSY ? res : 0;
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ res = __create_bufs(vdev->queue, p);
+ if (res == 0)
+ vdev->queue->owner = file->private_data;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
+
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_prepare_buf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
+
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
+ return vb2_querybuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
+
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_qbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
+
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
+
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_streamon(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
+
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_streamoff(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+
+/* v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_mmap(vdev->queue, vma);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_mmap);
+
+int vb2_fop_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (file->private_data == vdev->queue->owner) {
+ vb2_queue_release(vdev->queue);
+ vdev->queue->owner = NULL;
+ }
+ return v4l2_fh_release(file);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_release);
+
+ssize_t vb2_fop_write(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock;
+ int err = -EBUSY;
+
+ if (must_lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev, file))
+ goto exit;
+ err = vb2_write(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (err >= 0)
+ vdev->queue->owner = file->private_data;
+exit:
+ if (must_lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_write);
+
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock;
+ int err = -EBUSY;
+
+ if (must_lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev, file))
+ goto exit;
+ err = vb2_read(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (err >= 0)
+ vdev->queue->owner = file->private_data;
+exit:
+ if (must_lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_read);
+
+unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct vb2_queue *q = vdev->queue;
+ struct mutex *lock = q->lock ? q->lock : vdev->lock;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned res;
+ void *fileio;
+ /* Yuck. We really need to get rid of this flag asap. If it is
+ set, then the core took the serialization lock before calling
+ poll(). This is being phased out, but for now we have to handle
+ this case. */
+ bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
+ bool must_lock = false;
+
+ /* Try to be smart: only lock if polling might start fileio,
+ otherwise locking will only introduce unwanted delays. */
+ if (q->num_buffers == 0 && q->fileio == NULL) {
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+ (req_events & (POLLIN | POLLRDNORM)))
+ must_lock = true;
+ else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+ (req_events & (POLLOUT | POLLWRNORM)))
+ must_lock = true;
+ }
+
+ /* If locking is needed, but this helper doesn't know how, then you
+ shouldn't be using this helper but you should write your own. */
+ WARN_ON(must_lock && !locked && !lock);
+
+ if (must_lock && !locked && lock && mutex_lock_interruptible(lock))
+ return POLLERR;
+
+ fileio = q->fileio;
+
+ res = vb2_poll(vdev->queue, file, wait);
+
+ /* If fileio was started, then we have a new queue owner. */
+ if (must_lock && !fileio && q->fileio)
+ q->owner = file->private_data;
+ if (must_lock && !locked && lock)
+ mutex_unlock(lock);
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_poll);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
+#endif
+
+/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq)
+{
+ mutex_unlock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
+
+void vb2_ops_wait_finish(struct vb2_queue *vq)
+{
+ mutex_lock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
+
MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 08c10240e70f..a05494b71b20 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -188,6 +188,7 @@ struct vivi_dev {
struct list_head vivi_devlist;
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev;
/* controls */
struct v4l2_ctrl *brightness;
@@ -213,9 +214,6 @@ struct vivi_dev {
spinlock_t slock;
struct mutex mutex;
- /* various device info */
- struct video_device *vfd;
-
struct vivi_dmaqueue vidq;
/* Several counters */
@@ -232,7 +230,6 @@ struct vivi_dev {
struct vivi_fmt *fmt;
unsigned int width, height;
struct vb2_queue vb_vidq;
- enum v4l2_field field;
unsigned int field_count;
u8 bars[9][3];
@@ -625,7 +622,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->mv_count += 2;
- buf->vb.v4l2_buf.field = dev->field;
+ buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
dev->field_count++;
buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
do_gettimeofday(&ts);
@@ -769,7 +766,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
- size = dev->width * dev->height * dev->pixelsize;
+ if (fmt)
+ size = fmt->fmt.pix.sizeimage;
+ else
+ size = dev->width * dev->height * dev->pixelsize;
+
+ if (size == 0)
+ return -EINVAL;
if (0 == *nbuffers)
*nbuffers = 32;
@@ -792,27 +795,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
return 0;
}
-static int buffer_init(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
-
- BUG_ON(NULL == dev->fmt);
-
- /*
- * This callback is called once per buffer, after its allocation.
- *
- * Vivi does not allow changing format during streaming, but it is
- * possible to do so when streaming is paused (i.e. in streamoff state).
- * Buffers however are not freed when going into streamoff and so
- * buffer size verification has to be done in buffer_prepare, on each
- * qbuf.
- * It would be best to move verification code here to buf_init and
- * s_fmt though.
- */
-
- return 0;
-}
-
static int buffer_prepare(struct vb2_buffer *vb)
{
struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
@@ -850,20 +832,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
return 0;
}
-static int buffer_finish(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- dprintk(dev, 1, "%s\n", __func__);
- return 0;
-}
-
-static void buffer_cleanup(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- dprintk(dev, 1, "%s\n", __func__);
-
-}
-
static void buffer_queue(struct vb2_buffer *vb)
{
struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
@@ -909,10 +877,7 @@ static void vivi_unlock(struct vb2_queue *vq)
static struct vb2_ops vivi_video_qops = {
.queue_setup = queue_setup,
- .buf_init = buffer_init,
.buf_prepare = buffer_prepare,
- .buf_finish = buffer_finish,
- .buf_cleanup = buffer_cleanup,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
@@ -959,7 +924,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.field = dev->field;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -978,25 +943,16 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
{
struct vivi_dev *dev = video_drvdata(file);
struct vivi_fmt *fmt;
- enum v4l2_field field;
fmt = get_format(f);
if (!fmt) {
- dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n",
+ dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY) {
- field = V4L2_FIELD_INTERLACED;
- } else if (V4L2_FIELD_INTERLACED != field) {
- dprintk(dev, 1, "Field type invalid.\n");
- return -EINVAL;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ fmt = get_format(f);
}
- f->fmt.pix.field = field;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
&f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
f->fmt.pix.bytesperline =
@@ -1021,7 +977,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
if (ret < 0)
return ret;
- if (vb2_is_streaming(q)) {
+ if (vb2_is_busy(q)) {
dprintk(dev, 1, "%s device busy\n", __func__);
return -EBUSY;
}
@@ -1030,53 +986,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
dev->pixelsize = dev->fmt->depth / 8;
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
- dev->field = f->fmt.pix.field;
return 0;
}
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_reqbufs(&dev->vb_vidq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_querybuf(&dev->vb_vidq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_qbuf(&dev->vb_vidq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_streamon(&dev->vb_vidq, i);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct vivi_dev *dev = video_drvdata(file);
- return vb2_streamoff(&dev->vb_vidq, i);
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
-{
- return 0;
-}
-
/* only one input in this sample driver */
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
@@ -1085,7 +998,6 @@ static int vidioc_enum_input(struct file *file, void *priv,
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_525_60;
sprintf(inp->name, "Camera %u", inp->index);
return 0;
}
@@ -1145,58 +1057,6 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
File operations for the device
------------------------------------------------------------------*/
-static ssize_t
-vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct vivi_dev *dev = video_drvdata(file);
- int err;
-
- dprintk(dev, 1, "read called\n");
- mutex_lock(&dev->mutex);
- err = vb2_read(&dev->vb_vidq, data, count, ppos,
- file->f_flags & O_NONBLOCK);
- mutex_unlock(&dev->mutex);
- return err;
-}
-
-static unsigned int
-vivi_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct vivi_dev *dev = video_drvdata(file);
- struct vb2_queue *q = &dev->vb_vidq;
-
- dprintk(dev, 1, "%s\n", __func__);
- return vb2_poll(q, file, wait);
-}
-
-static int vivi_close(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct vivi_dev *dev = video_drvdata(file);
-
- dprintk(dev, 1, "close called (dev=%s), file %p\n",
- video_device_node_name(vdev), file);
-
- if (v4l2_fh_is_singular_file(file))
- vb2_queue_release(&dev->vb_vidq);
- return v4l2_fh_release(file);
-}
-
-static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct vivi_dev *dev = video_drvdata(file);
- int ret;
-
- dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
-
- ret = vb2_mmap(&dev->vb_vidq, vma);
- dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
- ret);
- return ret;
-}
-
static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
.g_volatile_ctrl = vivi_g_volatile_ctrl,
.s_ctrl = vivi_s_ctrl,
@@ -1301,11 +1161,11 @@ static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
- .release = vivi_close,
- .read = vivi_read,
- .poll = vivi_poll,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vivi_mmap,
+ .mmap = vb2_fop_mmap,
};
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
@@ -1314,16 +1174,17 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -1333,10 +1194,7 @@ static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
- .release = video_device_release,
-
- .tvnorms = V4L2_STD_525_60,
- .current_norm = V4L2_STD_NTSC_M,
+ .release = video_device_release_empty,
};
/* -----------------------------------------------------------------
@@ -1354,8 +1212,8 @@ static int vivi_release(void)
dev = list_entry(list, struct vivi_dev, vivi_devlist);
v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(dev->vfd));
- video_unregister_device(dev->vfd);
+ video_device_node_name(&dev->vdev));
+ video_unregister_device(&dev->vdev);
v4l2_device_unregister(&dev->v4l2_dev);
v4l2_ctrl_handler_free(&dev->ctrl_handler);
kfree(dev);
@@ -1440,14 +1298,11 @@ static int __init vivi_create_instance(int inst)
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
- ret = -ENOMEM;
- vfd = video_device_alloc();
- if (!vfd)
- goto unreg_dev;
-
+ vfd = &dev->vdev;
*vfd = vivi_template;
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->queue = q;
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
/*
@@ -1455,26 +1310,19 @@ static int __init vivi_create_instance(int inst)
* all fops and v4l2 ioctls.
*/
vfd->lock = &dev->mutex;
+ video_set_drvdata(vfd, dev);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
if (ret < 0)
- goto rel_vdev;
-
- video_set_drvdata(vfd, dev);
+ goto unreg_dev;
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
- if (video_nr != -1)
- video_nr++;
-
- dev->vfd = vfd;
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(vfd));
return 0;
-rel_vdev:
- video_device_release(vfd);
unreg_dev:
v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(&dev->v4l2_dev);
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index e44cb330bbc8..9afab35878b4 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -37,6 +37,10 @@
#include <linux/highmem.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include <media/videobuf-vmalloc.h>
@@ -120,11 +124,6 @@ static struct usb_device_id device_table[] = {
MODULE_DEVICE_TABLE(usb, device_table);
-struct zr364xx_mode {
- u32 color; /* output video color format */
- u32 brightness; /* brightness */
-};
-
/* frame structure */
struct zr364xx_framei {
unsigned long ulState; /* ulState:ZR364XX_READ_IDLE,
@@ -173,7 +172,10 @@ static const struct zr364xx_fmt formats[] = {
struct zr364xx_camera {
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface;/* the interface for this device */
- struct video_device *vdev; /* v4l video device */
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev; /* v4l video device */
+ struct v4l2_fh *owner; /* owns the streaming */
int nb;
struct zr364xx_bufferi buffer;
int skip;
@@ -181,12 +183,9 @@ struct zr364xx_camera {
int height;
int method;
struct mutex lock;
- struct mutex open_lock;
- int users;
spinlock_t slock;
struct zr364xx_dmaqueue vidq;
- int resources;
int last_frame;
int cur_frame;
unsigned long frame_count;
@@ -197,8 +196,7 @@ struct zr364xx_camera {
const struct zr364xx_fmt *fmt;
struct videobuf_queue vb_vidq;
- enum v4l2_buf_type type;
- struct zr364xx_mode mode;
+ bool was_streaming;
};
/* buffer for one video frame */
@@ -230,11 +228,6 @@ static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
transfer_buffer, size, CTRL_TIMEOUT);
kfree(transfer_buffer);
-
- if (status < 0)
- dev_err(&udev->dev,
- "Failed sending control message, error %d.\n", status);
-
return status;
}
@@ -468,6 +461,7 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
loff_t * ppos)
{
struct zr364xx_camera *cam = video_drvdata(file);
+ int err = 0;
_DBG("%s\n", __func__);
@@ -477,17 +471,21 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
if (!count)
return -EINVAL;
- if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- zr364xx_vidioc_streamon(file, cam, cam->type) == 0) {
- DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count,
- (int) *ppos);
+ if (mutex_lock_interruptible(&cam->lock))
+ return -ERESTARTSYS;
+
+ err = zr364xx_vidioc_streamon(file, file->private_data,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (err == 0) {
+ DBG("%s: reading %d bytes at pos %d.\n", __func__,
+ (int) count, (int) *ppos);
/* NoMan Sux ! */
- return videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
+ err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
file->f_flags & O_NONBLOCK);
}
-
- return 0;
+ mutex_unlock(&cam->lock);
+ return err;
}
/* video buffer vmalloc implementation based partly on VIVI driver which is
@@ -702,35 +700,6 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam,
return 0;
}
-static int res_get(struct zr364xx_camera *cam)
-{
- /* is it free? */
- mutex_lock(&cam->lock);
- if (cam->resources) {
- /* no, someone else uses it */
- mutex_unlock(&cam->lock);
- return 0;
- }
- /* it's free, grab it */
- cam->resources = 1;
- _DBG("res: get\n");
- mutex_unlock(&cam->lock);
- return 1;
-}
-
-static inline int res_check(struct zr364xx_camera *cam)
-{
- return cam->resources;
-}
-
-static void res_free(struct zr364xx_camera *cam)
-{
- mutex_lock(&cam->lock);
- cam->resources = 0;
- mutex_unlock(&cam->lock);
- _DBG("res: put\n");
-}
-
static int zr364xx_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -740,9 +709,10 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -772,50 +742,18 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv,
return 0;
}
-static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
+static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct zr364xx_camera *cam;
-
- if (file == NULL)
- return -ENODEV;
- cam = video_drvdata(file);
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->type = V4L2_CTRL_TYPE_INTEGER;
- strcpy(c->name, "Brightness");
- c->minimum = 0;
- c->maximum = 127;
- c->step = 1;
- c->default_value = cam->mode.brightness;
- c->flags = 0;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *c)
-{
- struct zr364xx_camera *cam;
+ struct zr364xx_camera *cam =
+ container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler);
int temp;
- if (file == NULL)
- return -ENODEV;
- cam = video_drvdata(file);
-
- switch (c->id) {
+ switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- cam->mode.brightness = c->value;
/* hardware brightness */
- mutex_lock(&cam->lock);
send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
- temp = (0x60 << 8) + 127 - cam->mode.brightness;
+ temp = (0x60 << 8) + 127 - ctrl->val;
send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
- mutex_unlock(&cam->lock);
break;
default:
return -EINVAL;
@@ -824,25 +762,6 @@ static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
return 0;
}
-static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *c)
-{
- struct zr364xx_camera *cam;
-
- if (file == NULL)
- return -ENODEV;
- cam = video_drvdata(file);
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value = cam->mode.brightness;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_fmtdesc *f)
{
@@ -888,7 +807,7 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.colorspace = 0;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
f->fmt.pix.priv = 0;
DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
@@ -911,7 +830,7 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.height = cam->height;
f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.colorspace = 0;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
f->fmt.pix.priv = 0;
return 0;
}
@@ -936,7 +855,7 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
goto out;
}
- if (res_check(cam)) {
+ if (cam->owner) {
DBG("%s can't change format after started\n", __func__);
ret = -EBUSY;
goto out;
@@ -944,14 +863,13 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
cam->width = f->fmt.pix.width;
cam->height = f->fmt.pix.height;
- dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__,
+ DBG("%s: %dx%d mode selected\n", __func__,
cam->width, cam->height);
f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.colorspace = 0;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
f->fmt.pix.priv = 0;
cam->vb_vidq.field = f->fmt.pix.field;
- cam->mode.color = V4L2_PIX_FMT_JPEG;
if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120)
mode = 1;
@@ -1015,10 +933,11 @@ out:
static int zr364xx_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
- int rc;
struct zr364xx_camera *cam = video_drvdata(file);
- rc = videobuf_reqbufs(&cam->vb_vidq, p);
- return rc;
+
+ if (cam->owner && cam->owner != priv)
+ return -EBUSY;
+ return videobuf_reqbufs(&cam->vb_vidq, p);
}
static int zr364xx_vidioc_querybuf(struct file *file,
@@ -1038,6 +957,8 @@ static int zr364xx_vidioc_qbuf(struct file *file,
int rc;
struct zr364xx_camera *cam = video_drvdata(file);
_DBG("%s\n", __func__);
+ if (cam->owner && cam->owner != priv)
+ return -EBUSY;
rc = videobuf_qbuf(&cam->vb_vidq, p);
return rc;
}
@@ -1049,6 +970,8 @@ static int zr364xx_vidioc_dqbuf(struct file *file,
int rc;
struct zr364xx_camera *cam = video_drvdata(file);
_DBG("%s\n", __func__);
+ if (cam->owner && cam->owner != priv)
+ return -EBUSY;
rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK);
return rc;
}
@@ -1197,29 +1120,23 @@ static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam)
return 0;
}
-static int zr364xx_vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
+static int zr364xx_prepare(struct zr364xx_camera *cam)
{
- struct zr364xx_camera *cam = video_drvdata(file);
- int j;
int res;
+ int i, j;
- DBG("%s\n", __func__);
-
- if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&cam->udev->dev, "invalid fh type0\n");
- return -EINVAL;
- }
- if (cam->type != type) {
- dev_err(&cam->udev->dev, "invalid fh type1\n");
- return -EINVAL;
- }
-
- if (!res_get(cam)) {
- dev_err(&cam->udev->dev, "stream busy\n");
- return -EBUSY;
+ for (i = 0; init[cam->method][i].size != -1; i++) {
+ res = send_control_msg(cam->udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
+ if (res < 0) {
+ dev_err(&cam->udev->dev,
+ "error during open sequence: %d\n", i);
+ return res;
+ }
}
+ cam->skip = 2;
cam->last_frame = -1;
cam->cur_frame = 0;
cam->frame_count = 0;
@@ -1227,11 +1144,31 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv,
cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
cam->buffer.frame[j].cur_size = 0;
}
+ v4l2_ctrl_handler_setup(&cam->ctrl_handler);
+ return 0;
+}
+
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ int res;
+
+ DBG("%s\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->owner && cam->owner != priv)
+ return -EBUSY;
+
+ res = zr364xx_prepare(cam);
+ if (res)
+ return res;
res = videobuf_streamon(&cam->vb_vidq);
if (res == 0) {
zr364xx_start_acquire(cam);
- } else {
- res_free(cam);
+ cam->owner = file->private_data;
}
return res;
}
@@ -1239,67 +1176,32 @@ static int zr364xx_vidioc_streamon(struct file *file, void *priv,
static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- int res;
struct zr364xx_camera *cam = video_drvdata(file);
DBG("%s\n", __func__);
- if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&cam->udev->dev, "invalid fh type0\n");
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- }
- if (cam->type != type) {
- dev_err(&cam->udev->dev, "invalid fh type1\n");
- return -EINVAL;
- }
+ if (cam->owner && cam->owner != priv)
+ return -EBUSY;
zr364xx_stop_acquire(cam);
- res = videobuf_streamoff(&cam->vb_vidq);
- if (res < 0)
- return res;
- res_free(cam);
- return 0;
+ return videobuf_streamoff(&cam->vb_vidq);
}
/* open the camera */
static int zr364xx_open(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
struct zr364xx_camera *cam = video_drvdata(file);
- struct usb_device *udev = cam->udev;
- int i, err;
+ int err;
DBG("%s\n", __func__);
- mutex_lock(&cam->open_lock);
+ if (mutex_lock_interruptible(&cam->lock))
+ return -ERESTARTSYS;
- if (cam->users) {
- err = -EBUSY;
+ err = v4l2_fh_open(file);
+ if (err)
goto out;
- }
-
- for (i = 0; init[cam->method][i].size != -1; i++) {
- err =
- send_control_msg(udev, 1, init[cam->method][i].value,
- 0, init[cam->method][i].bytes,
- init[cam->method][i].size);
- if (err < 0) {
- dev_err(&cam->udev->dev,
- "error during open sequence: %d\n", i);
- goto out;
- }
- }
-
- cam->skip = 2;
- cam->users++;
- file->private_data = vdev;
- cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cam->fmt = formats;
-
- videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
- NULL, &cam->slock,
- cam->type,
- V4L2_FIELD_NONE,
- sizeof(struct zr364xx_buffer), cam, NULL);
/* Added some delay here, since opening/closing the camera quickly,
* like Ekiga does during its startup, can crash the webcam
@@ -1308,29 +1210,20 @@ static int zr364xx_open(struct file *file)
err = 0;
out:
- mutex_unlock(&cam->open_lock);
+ mutex_unlock(&cam->lock);
DBG("%s: %d\n", __func__, err);
return err;
}
-static void zr364xx_destroy(struct zr364xx_camera *cam)
+static void zr364xx_release(struct v4l2_device *v4l2_dev)
{
+ struct zr364xx_camera *cam =
+ container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev);
unsigned long i;
- if (!cam) {
- printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__);
- return;
- }
- mutex_lock(&cam->open_lock);
- if (cam->vdev)
- video_unregister_device(cam->vdev);
- cam->vdev = NULL;
-
- /* stops the read pipe if it is running */
- if (cam->b_acquire)
- zr364xx_stop_acquire(cam);
+ v4l2_device_unregister(&cam->v4l2_dev);
- zr364xx_stop_readpipe(cam);
+ videobuf_mmap_free(&cam->vb_vidq);
/* release sys buffers */
for (i = 0; i < FRAMES; i++) {
@@ -1341,62 +1234,45 @@ static void zr364xx_destroy(struct zr364xx_camera *cam)
cam->buffer.frame[i].lpvbits = NULL;
}
+ v4l2_ctrl_handler_free(&cam->ctrl_handler);
/* release transfer buffer */
kfree(cam->pipe->transfer_buffer);
- cam->pipe->transfer_buffer = NULL;
- mutex_unlock(&cam->open_lock);
kfree(cam);
- cam = NULL;
}
/* release the camera */
-static int zr364xx_release(struct file *file)
+static int zr364xx_close(struct file *file)
{
struct zr364xx_camera *cam;
struct usb_device *udev;
- int i, err;
+ int i;
DBG("%s\n", __func__);
cam = video_drvdata(file);
- if (!cam)
- return -ENODEV;
-
- mutex_lock(&cam->open_lock);
+ mutex_lock(&cam->lock);
udev = cam->udev;
- /* turn off stream */
- if (res_check(cam)) {
+ if (file->private_data == cam->owner) {
+ /* turn off stream */
if (cam->b_acquire)
zr364xx_stop_acquire(cam);
videobuf_streamoff(&cam->vb_vidq);
- res_free(cam);
- }
-
- cam->users--;
- file->private_data = NULL;
- for (i = 0; i < 2; i++) {
- err =
- send_control_msg(udev, 1, init[cam->method][i].value,
- 0, init[cam->method][i].bytes,
- init[cam->method][i].size);
- if (err < 0) {
- dev_err(&udev->dev, "error during release sequence\n");
- goto out;
+ for (i = 0; i < 2; i++) {
+ send_control_msg(udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
}
+ cam->owner = NULL;
}
/* Added some delay here, since opening/closing the camera quickly,
* like Ekiga does during its startup, can crash the webcam
*/
mdelay(100);
- err = 0;
-
-out:
- mutex_unlock(&cam->open_lock);
-
- return err;
+ mutex_unlock(&cam->lock);
+ return v4l2_fh_release(file);
}
@@ -1424,21 +1300,24 @@ static unsigned int zr364xx_poll(struct file *file,
{
struct zr364xx_camera *cam = video_drvdata(file);
struct videobuf_queue *q = &cam->vb_vidq;
- _DBG("%s\n", __func__);
+ unsigned res = v4l2_ctrl_poll(file, wait);
- if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return POLLERR;
+ _DBG("%s\n", __func__);
- return videobuf_poll_stream(file, q, wait);
+ return res | videobuf_poll_stream(file, q, wait);
}
+static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = {
+ .s_ctrl = zr364xx_s_ctrl,
+};
+
static const struct v4l2_file_operations zr364xx_fops = {
.owner = THIS_MODULE,
.open = zr364xx_open,
- .release = zr364xx_release,
+ .release = zr364xx_close,
.read = zr364xx_read,
.mmap = zr364xx_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = video_ioctl2,
.poll = zr364xx_poll,
};
@@ -1453,20 +1332,20 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = {
.vidioc_s_input = zr364xx_vidioc_s_input,
.vidioc_streamon = zr364xx_vidioc_streamon,
.vidioc_streamoff = zr364xx_vidioc_streamoff,
- .vidioc_queryctrl = zr364xx_vidioc_queryctrl,
- .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl,
- .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl,
.vidioc_reqbufs = zr364xx_vidioc_reqbufs,
.vidioc_querybuf = zr364xx_vidioc_querybuf,
.vidioc_qbuf = zr364xx_vidioc_qbuf,
.vidioc_dqbuf = zr364xx_vidioc_dqbuf,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device zr364xx_template = {
.name = DRIVER_DESC,
.fops = &zr364xx_fops,
.ioctl_ops = &zr364xx_ioctl_ops,
- .release = video_device_release,
+ .release = video_device_release_empty,
};
@@ -1540,6 +1419,7 @@ static int zr364xx_probe(struct usb_interface *intf,
struct zr364xx_camera *cam = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
+ struct v4l2_ctrl_handler *hdl;
int err;
int i;
@@ -1555,21 +1435,34 @@ static int zr364xx_probe(struct usb_interface *intf,
dev_err(&udev->dev, "cam: out of memory !\n");
return -ENOMEM;
}
- /* save the init method used by this camera */
- cam->method = id->driver_info;
- cam->vdev = video_device_alloc();
- if (cam->vdev == NULL) {
- dev_err(&udev->dev, "cam->vdev: out of memory !\n");
+ cam->v4l2_dev.release = zr364xx_release;
+ err = v4l2_device_register(&intf->dev, &cam->v4l2_dev);
+ if (err < 0) {
+ dev_err(&udev->dev, "couldn't register v4l2_device\n");
kfree(cam);
- cam = NULL;
- return -ENOMEM;
+ return err;
}
- memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
- cam->vdev->parent = &intf->dev;
- video_set_drvdata(cam->vdev, cam);
+ hdl = &cam->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 1);
+ v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 127, 1, 64);
+ if (hdl->error) {
+ err = hdl->error;
+ dev_err(&udev->dev, "couldn't register control\n");
+ goto fail;
+ }
+ /* save the init method used by this camera */
+ cam->method = id->driver_info;
+ mutex_init(&cam->lock);
+ cam->vdev = zr364xx_template;
+ cam->vdev.lock = &cam->lock;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ cam->vdev.ctrl_handler = &cam->ctrl_handler;
+ set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
+ video_set_drvdata(&cam->vdev, cam);
if (debug)
- cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+ cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
cam->udev = udev;
@@ -1615,11 +1508,7 @@ static int zr364xx_probe(struct usb_interface *intf,
header2[439] = cam->width / 256;
header2[440] = cam->width % 256;
- cam->users = 0;
cam->nb = 0;
- cam->mode.brightness = 64;
- mutex_init(&cam->lock);
- mutex_init(&cam->open_lock);
DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf);
@@ -1635,52 +1524,100 @@ static int zr364xx_probe(struct usb_interface *intf,
}
if (!cam->read_endpoint) {
+ err = -ENOMEM;
dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
- video_device_release(cam->vdev);
- kfree(cam);
- cam = NULL;
- return -ENOMEM;
+ goto fail;
}
/* v4l */
INIT_LIST_HEAD(&cam->vidq.active);
cam->vidq.cam = cam;
- err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
- if (err) {
- dev_err(&udev->dev, "video_register_device failed\n");
- video_device_release(cam->vdev);
- kfree(cam);
- cam = NULL;
- return err;
- }
usb_set_intfdata(intf, cam);
/* load zr364xx board specific */
err = zr364xx_board_init(cam);
- if (err) {
- spin_lock_init(&cam->slock);
- return err;
- }
+ if (!err)
+ err = v4l2_ctrl_handler_setup(hdl);
+ if (err)
+ goto fail;
spin_lock_init(&cam->slock);
+ cam->fmt = formats;
+
+ videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
+ NULL, &cam->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct zr364xx_buffer), cam, &cam->lock);
+
+ err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (err) {
+ dev_err(&udev->dev, "video_register_device failed\n");
+ goto fail;
+ }
+
dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n",
- video_device_node_name(cam->vdev));
+ video_device_node_name(&cam->vdev));
return 0;
+
+fail:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&cam->v4l2_dev);
+ kfree(cam);
+ return err;
}
static void zr364xx_disconnect(struct usb_interface *intf)
{
struct zr364xx_camera *cam = usb_get_intfdata(intf);
- videobuf_mmap_free(&cam->vb_vidq);
+
+ mutex_lock(&cam->lock);
usb_set_intfdata(intf, NULL);
dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n");
- zr364xx_destroy(cam);
+ video_unregister_device(&cam->vdev);
+ v4l2_device_disconnect(&cam->v4l2_dev);
+
+ /* stops the read pipe if it is running */
+ if (cam->b_acquire)
+ zr364xx_stop_acquire(cam);
+
+ zr364xx_stop_readpipe(cam);
+ mutex_unlock(&cam->lock);
+ v4l2_device_put(&cam->v4l2_dev);
}
+#ifdef CONFIG_PM
+static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct zr364xx_camera *cam = usb_get_intfdata(intf);
+
+ cam->was_streaming = cam->b_acquire;
+ if (!cam->was_streaming)
+ return 0;
+ zr364xx_stop_acquire(cam);
+ zr364xx_stop_readpipe(cam);
+ return 0;
+}
+
+static int zr364xx_resume(struct usb_interface *intf)
+{
+ struct zr364xx_camera *cam = usb_get_intfdata(intf);
+ int res;
+
+ if (!cam->was_streaming)
+ return 0;
+
+ zr364xx_start_readpipe(cam);
+ res = zr364xx_prepare(cam);
+ if (!res)
+ zr364xx_start_acquire(cam);
+ return res;
+}
+#endif
/**********************/
/* Module integration */
@@ -1690,6 +1627,11 @@ static struct usb_driver zr364xx_driver = {
.name = "zr364xx",
.probe = zr364xx_probe,
.disconnect = zr364xx_disconnect,
+#ifdef CONFIG_PM
+ .suspend = zr364xx_suspend,
+ .resume = zr364xx_resume,
+ .reset_resume = zr364xx_resume,
+#endif
.id_table = device_table
};
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index 098de2b35784..9a49c243a6ac 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -188,6 +188,13 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
if (!dev)
return -ENXIO;
+ /*
+ * Stop users being able to try and allocate arbitary amounts
+ * of DMA space. 64K is way more than sufficient for this.
+ */
+ if (kcmd.oplen > 65536)
+ return -EMSGSIZE;
+
ops = memdup_user(kcmd.opbuf, kcmd.oplen);
if (IS_ERR(ops))
return PTR_ERR(ops);
diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c
index 506c36f6e1db..8001aa6bfb48 100644
--- a/drivers/message/i2o/i2o_proc.c
+++ b/drivers/message/i2o/i2o_proc.c
@@ -255,9 +255,8 @@ static char *scsi_devices[] = {
"Array Controller Device"
};
-static char *chtostr(u8 * chars, int n)
+static char *chtostr(char *tmp, u8 *chars, int n)
{
- char tmp[256];
tmp[0] = 0;
return strncat(tmp, (char *)chars, n);
}
@@ -791,6 +790,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
} *result;
i2o_exec_execute_ddm_table ddm_table;
+ char tmp[28 + 1];
result = kmalloc(sizeof(*result), GFP_KERNEL);
if (!result)
@@ -826,7 +826,7 @@ static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id);
seq_printf(seq, "%-#8x", ddm_table.module_id);
seq_printf(seq, "%-29s",
- chtostr(ddm_table.module_name_version, 28));
+ chtostr(tmp, ddm_table.module_name_version, 28));
seq_printf(seq, "%9d ", ddm_table.data_size);
seq_printf(seq, "%8d", ddm_table.code_size);
@@ -893,6 +893,7 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
i2o_driver_result_table *result;
i2o_driver_store_table *dst;
+ char tmp[28 + 1];
result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL);
if (result == NULL)
@@ -927,8 +928,9 @@ static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
seq_printf(seq, "%-#7x", dst->i2o_vendor_id);
seq_printf(seq, "%-#8x", dst->module_id);
- seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28));
- seq_printf(seq, "%-9s", chtostr(dst->date, 8));
+ seq_printf(seq, "%-29s",
+ chtostr(tmp, dst->module_name_version, 28));
+ seq_printf(seq, "%-9s", chtostr(tmp, dst->date, 8));
seq_printf(seq, "%8d ", dst->module_size);
seq_printf(seq, "%8d ", dst->mpb_size);
seq_printf(seq, "0x%04x", dst->module_flags);
@@ -1248,6 +1250,7 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
// == (allow) 512d bytes (max)
static u16 *work16 = (u16 *) work32;
int token;
+ char tmp[16 + 1];
token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32));
@@ -1260,13 +1263,13 @@ static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
seq_printf(seq, "Owner TID : %0#5x\n", work16[2]);
seq_printf(seq, "Parent TID : %0#5x\n", work16[3]);
seq_printf(seq, "Vendor info : %s\n",
- chtostr((u8 *) (work32 + 2), 16));
+ chtostr(tmp, (u8 *) (work32 + 2), 16));
seq_printf(seq, "Product info : %s\n",
- chtostr((u8 *) (work32 + 6), 16));
+ chtostr(tmp, (u8 *) (work32 + 6), 16));
seq_printf(seq, "Description : %s\n",
- chtostr((u8 *) (work32 + 10), 16));
+ chtostr(tmp, (u8 *) (work32 + 10), 16));
seq_printf(seq, "Product rev. : %s\n",
- chtostr((u8 *) (work32 + 14), 8));
+ chtostr(tmp, (u8 *) (work32 + 14), 8));
seq_printf(seq, "Serial number : ");
print_serial_number(seq, (u8 *) (work32 + 16),
@@ -1303,6 +1306,8 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
u8 pad[256]; // allow up to 256 byte (max) serial number
} result;
+ char tmp[24 + 1];
+
token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result));
if (token < 0) {
@@ -1312,9 +1317,9 @@ static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid);
seq_printf(seq, "Module name : %s\n",
- chtostr(result.module_name, 24));
+ chtostr(tmp, result.module_name, 24));
seq_printf(seq, "Module revision : %s\n",
- chtostr(result.module_rev, 8));
+ chtostr(tmp, result.module_rev, 8));
seq_printf(seq, "Serial number : ");
print_serial_number(seq, result.serial_number, sizeof(result) - 36);
@@ -1338,6 +1343,8 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
u8 instance_number[4];
} result;
+ char tmp[64 + 1];
+
token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result));
if (token < 0) {
@@ -1346,13 +1353,13 @@ static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
}
seq_printf(seq, "Device name : %s\n",
- chtostr(result.device_name, 64));
+ chtostr(tmp, result.device_name, 64));
seq_printf(seq, "Service name : %s\n",
- chtostr(result.service_name, 64));
+ chtostr(tmp, result.service_name, 64));
seq_printf(seq, "Physical name : %s\n",
- chtostr(result.physical_location, 64));
+ chtostr(tmp, result.physical_location, 64));
seq_printf(seq, "Instance number : %s\n",
- chtostr(result.instance_number, 4));
+ chtostr(tmp, result.instance_number, 4));
return 0;
}
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
new file mode 100644
index 000000000000..b67a3018b136
--- /dev/null
+++ b/drivers/mfd/88pm800.c
@@ -0,0 +1,596 @@
+/*
+ * Base driver for Marvell 88PM800
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+
+#define PM800_CHIP_ID (0x00)
+
+/* Interrupt Registers */
+#define PM800_INT_STATUS1 (0x05)
+#define PM800_ONKEY_INT_STS1 (1 << 0)
+#define PM800_EXTON_INT_STS1 (1 << 1)
+#define PM800_CHG_INT_STS1 (1 << 2)
+#define PM800_BAT_INT_STS1 (1 << 3)
+#define PM800_RTC_INT_STS1 (1 << 4)
+#define PM800_CLASSD_OC_INT_STS1 (1 << 5)
+
+#define PM800_INT_STATUS2 (0x06)
+#define PM800_VBAT_INT_STS2 (1 << 0)
+#define PM800_VSYS_INT_STS2 (1 << 1)
+#define PM800_VCHG_INT_STS2 (1 << 2)
+#define PM800_TINT_INT_STS2 (1 << 3)
+#define PM800_GPADC0_INT_STS2 (1 << 4)
+#define PM800_TBAT_INT_STS2 (1 << 5)
+#define PM800_GPADC2_INT_STS2 (1 << 6)
+#define PM800_GPADC3_INT_STS2 (1 << 7)
+
+#define PM800_INT_STATUS3 (0x07)
+
+#define PM800_INT_STATUS4 (0x08)
+#define PM800_GPIO0_INT_STS4 (1 << 0)
+#define PM800_GPIO1_INT_STS4 (1 << 1)
+#define PM800_GPIO2_INT_STS4 (1 << 2)
+#define PM800_GPIO3_INT_STS4 (1 << 3)
+#define PM800_GPIO4_INT_STS4 (1 << 4)
+
+#define PM800_INT_ENA_1 (0x09)
+#define PM800_ONKEY_INT_ENA1 (1 << 0)
+#define PM800_EXTON_INT_ENA1 (1 << 1)
+#define PM800_CHG_INT_ENA1 (1 << 2)
+#define PM800_BAT_INT_ENA1 (1 << 3)
+#define PM800_RTC_INT_ENA1 (1 << 4)
+#define PM800_CLASSD_OC_INT_ENA1 (1 << 5)
+
+#define PM800_INT_ENA_2 (0x0A)
+#define PM800_VBAT_INT_ENA2 (1 << 0)
+#define PM800_VSYS_INT_ENA2 (1 << 1)
+#define PM800_VCHG_INT_ENA2 (1 << 2)
+#define PM800_TINT_INT_ENA2 (1 << 3)
+
+#define PM800_INT_ENA_3 (0x0B)
+#define PM800_GPADC0_INT_ENA3 (1 << 0)
+#define PM800_GPADC1_INT_ENA3 (1 << 1)
+#define PM800_GPADC2_INT_ENA3 (1 << 2)
+#define PM800_GPADC3_INT_ENA3 (1 << 3)
+#define PM800_GPADC4_INT_ENA3 (1 << 4)
+
+#define PM800_INT_ENA_4 (0x0C)
+#define PM800_GPIO0_INT_ENA4 (1 << 0)
+#define PM800_GPIO1_INT_ENA4 (1 << 1)
+#define PM800_GPIO2_INT_ENA4 (1 << 2)
+#define PM800_GPIO3_INT_ENA4 (1 << 3)
+#define PM800_GPIO4_INT_ENA4 (1 << 4)
+
+/* number of INT_ENA & INT_STATUS regs */
+#define PM800_INT_REG_NUM (4)
+
+/* Interrupt Number in 88PM800 */
+enum {
+ PM800_IRQ_ONKEY, /*EN1b0 *//*0 */
+ PM800_IRQ_EXTON, /*EN1b1 */
+ PM800_IRQ_CHG, /*EN1b2 */
+ PM800_IRQ_BAT, /*EN1b3 */
+ PM800_IRQ_RTC, /*EN1b4 */
+ PM800_IRQ_CLASSD, /*EN1b5 *//*5 */
+ PM800_IRQ_VBAT, /*EN2b0 */
+ PM800_IRQ_VSYS, /*EN2b1 */
+ PM800_IRQ_VCHG, /*EN2b2 */
+ PM800_IRQ_TINT, /*EN2b3 */
+ PM800_IRQ_GPADC0, /*EN3b0 *//*10 */
+ PM800_IRQ_GPADC1, /*EN3b1 */
+ PM800_IRQ_GPADC2, /*EN3b2 */
+ PM800_IRQ_GPADC3, /*EN3b3 */
+ PM800_IRQ_GPADC4, /*EN3b4 */
+ PM800_IRQ_GPIO0, /*EN4b0 *//*15 */
+ PM800_IRQ_GPIO1, /*EN4b1 */
+ PM800_IRQ_GPIO2, /*EN4b2 */
+ PM800_IRQ_GPIO3, /*EN4b3 */
+ PM800_IRQ_GPIO4, /*EN4b4 *//*19 */
+ PM800_MAX_IRQ,
+};
+
+enum {
+ /* Procida */
+ PM800_CHIP_A0 = 0x60,
+ PM800_CHIP_A1 = 0x61,
+ PM800_CHIP_B0 = 0x62,
+ PM800_CHIP_C0 = 0x63,
+ PM800_CHIP_END = PM800_CHIP_C0,
+
+ /* Make sure to update this to the last stepping */
+ PM8XXX_CHIP_END = PM800_CHIP_END
+};
+
+static const struct i2c_device_id pm80x_id_table[] = {
+ {"88PM800", CHIP_PM800},
+ {} /* NULL terminated */
+};
+MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
+
+static struct resource rtc_resources[] = {
+ {
+ .name = "88pm80x-rtc",
+ .start = PM800_IRQ_RTC,
+ .end = PM800_IRQ_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell rtc_devs[] = {
+ {
+ .name = "88pm80x-rtc",
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resources = &rtc_resources[0],
+ .id = -1,
+ },
+};
+
+static struct resource onkey_resources[] = {
+ {
+ .name = "88pm80x-onkey",
+ .start = PM800_IRQ_ONKEY,
+ .end = PM800_IRQ_ONKEY,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell onkey_devs[] = {
+ {
+ .name = "88pm80x-onkey",
+ .num_resources = 1,
+ .resources = &onkey_resources[0],
+ .id = -1,
+ },
+};
+
+static const struct regmap_irq pm800_irqs[] = {
+ /* INT0 */
+ [PM800_IRQ_ONKEY] = {
+ .mask = PM800_ONKEY_INT_ENA1,
+ },
+ [PM800_IRQ_EXTON] = {
+ .mask = PM800_EXTON_INT_ENA1,
+ },
+ [PM800_IRQ_CHG] = {
+ .mask = PM800_CHG_INT_ENA1,
+ },
+ [PM800_IRQ_BAT] = {
+ .mask = PM800_BAT_INT_ENA1,
+ },
+ [PM800_IRQ_RTC] = {
+ .mask = PM800_RTC_INT_ENA1,
+ },
+ [PM800_IRQ_CLASSD] = {
+ .mask = PM800_CLASSD_OC_INT_ENA1,
+ },
+ /* INT1 */
+ [PM800_IRQ_VBAT] = {
+ .reg_offset = 1,
+ .mask = PM800_VBAT_INT_ENA2,
+ },
+ [PM800_IRQ_VSYS] = {
+ .reg_offset = 1,
+ .mask = PM800_VSYS_INT_ENA2,
+ },
+ [PM800_IRQ_VCHG] = {
+ .reg_offset = 1,
+ .mask = PM800_VCHG_INT_ENA2,
+ },
+ [PM800_IRQ_TINT] = {
+ .reg_offset = 1,
+ .mask = PM800_TINT_INT_ENA2,
+ },
+ /* INT2 */
+ [PM800_IRQ_GPADC0] = {
+ .reg_offset = 2,
+ .mask = PM800_GPADC0_INT_ENA3,
+ },
+ [PM800_IRQ_GPADC1] = {
+ .reg_offset = 2,
+ .mask = PM800_GPADC1_INT_ENA3,
+ },
+ [PM800_IRQ_GPADC2] = {
+ .reg_offset = 2,
+ .mask = PM800_GPADC2_INT_ENA3,
+ },
+ [PM800_IRQ_GPADC3] = {
+ .reg_offset = 2,
+ .mask = PM800_GPADC3_INT_ENA3,
+ },
+ [PM800_IRQ_GPADC4] = {
+ .reg_offset = 2,
+ .mask = PM800_GPADC4_INT_ENA3,
+ },
+ /* INT3 */
+ [PM800_IRQ_GPIO0] = {
+ .reg_offset = 3,
+ .mask = PM800_GPIO0_INT_ENA4,
+ },
+ [PM800_IRQ_GPIO1] = {
+ .reg_offset = 3,
+ .mask = PM800_GPIO1_INT_ENA4,
+ },
+ [PM800_IRQ_GPIO2] = {
+ .reg_offset = 3,
+ .mask = PM800_GPIO2_INT_ENA4,
+ },
+ [PM800_IRQ_GPIO3] = {
+ .reg_offset = 3,
+ .mask = PM800_GPIO3_INT_ENA4,
+ },
+ [PM800_IRQ_GPIO4] = {
+ .reg_offset = 3,
+ .mask = PM800_GPIO4_INT_ENA4,
+ },
+};
+
+static int __devinit device_gpadc_init(struct pm80x_chip *chip,
+ struct pm80x_platform_data *pdata)
+{
+ struct pm80x_subchip *subchip = chip->subchip;
+ struct regmap *map = subchip->regmap_gpadc;
+ int data = 0, mask = 0, ret = 0;
+
+ if (!map) {
+ dev_warn(chip->dev,
+ "Warning: gpadc regmap is not available!\n");
+ return -EINVAL;
+ }
+ /*
+ * initialize GPADC without activating it turn on GPADC
+ * measurments
+ */
+ ret = regmap_update_bits(map,
+ PM800_GPADC_MISC_CONFIG2,
+ PM800_GPADC_MISC_GPFSM_EN,
+ PM800_GPADC_MISC_GPFSM_EN);
+ if (ret < 0)
+ goto out;
+ /*
+ * This function configures the ADC as requires for
+ * CP implementation.CP does not "own" the ADC configuration
+ * registers and relies on AP.
+ * Reason: enable automatic ADC measurements needed
+ * for CP to get VBAT and RF temperature readings.
+ */
+ ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
+ PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
+ if (ret < 0)
+ goto out;
+ ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
+ (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
+ (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
+ if (ret < 0)
+ goto out;
+
+ /*
+ * the defult of PM800 is GPADC operates at 100Ks/s rate
+ * and Number of GPADC slots with active current bias prior
+ * to GPADC sampling = 1 slot for all GPADCs set for
+ * Temprature mesurmants
+ */
+ mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
+ PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
+
+ if (pdata && (pdata->batt_det == 0))
+ data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
+ PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
+ else
+ data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
+ PM800_GPADC_GP_BIAS_EN3);
+
+ ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
+ if (ret < 0)
+ goto out;
+
+ dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
+ return 0;
+
+out:
+ dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
+ return ret;
+}
+
+static int __devinit device_irq_init_800(struct pm80x_chip *chip)
+{
+ struct regmap *map = chip->regmap;
+ unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ int data, mask, ret = -EINVAL;
+
+ if (!map || !chip->irq) {
+ dev_err(chip->dev, "incorrect parameters\n");
+ return -EINVAL;
+ }
+
+ /*
+ * irq_mode defines the way of clearing interrupt. it's read-clear by
+ * default.
+ */
+ mask =
+ PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
+ PM800_WAKEUP2_INT_MASK;
+
+ data = PM800_WAKEUP2_INT_CLEAR;
+ ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
+
+ if (ret < 0)
+ goto out;
+
+ ret =
+ regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
+ chip->regmap_irq_chip, &chip->irq_data);
+
+out:
+ return ret;
+}
+
+static void device_irq_exit_800(struct pm80x_chip *chip)
+{
+ regmap_del_irq_chip(chip->irq, chip->irq_data);
+}
+
+static struct regmap_irq_chip pm800_irq_chip = {
+ .name = "88pm800",
+ .irqs = pm800_irqs,
+ .num_irqs = ARRAY_SIZE(pm800_irqs),
+
+ .num_regs = 4,
+ .status_base = PM800_INT_STATUS1,
+ .mask_base = PM800_INT_ENA_1,
+ .ack_base = PM800_INT_STATUS1,
+};
+
+static int pm800_pages_init(struct pm80x_chip *chip)
+{
+ struct pm80x_subchip *subchip;
+ struct i2c_client *client = chip->client;
+
+ subchip = chip->subchip;
+ /* PM800 block power: i2c addr 0x31 */
+ if (subchip->power_page_addr) {
+ subchip->power_page =
+ i2c_new_dummy(client->adapter, subchip->power_page_addr);
+ subchip->regmap_power =
+ devm_regmap_init_i2c(subchip->power_page,
+ &pm80x_regmap_config);
+ i2c_set_clientdata(subchip->power_page, chip);
+ } else
+ dev_info(chip->dev,
+ "PM800 block power 0x31: No power_page_addr\n");
+
+ /* PM800 block GPADC: i2c addr 0x32 */
+ if (subchip->gpadc_page_addr) {
+ subchip->gpadc_page = i2c_new_dummy(client->adapter,
+ subchip->gpadc_page_addr);
+ subchip->regmap_gpadc =
+ devm_regmap_init_i2c(subchip->gpadc_page,
+ &pm80x_regmap_config);
+ i2c_set_clientdata(subchip->gpadc_page, chip);
+ } else
+ dev_info(chip->dev,
+ "PM800 block GPADC 0x32: No gpadc_page_addr\n");
+
+ return 0;
+}
+
+static void pm800_pages_exit(struct pm80x_chip *chip)
+{
+ struct pm80x_subchip *subchip;
+
+ regmap_exit(chip->regmap);
+ i2c_unregister_device(chip->client);
+
+ subchip = chip->subchip;
+ if (subchip->power_page) {
+ regmap_exit(subchip->regmap_power);
+ i2c_unregister_device(subchip->power_page);
+ }
+ if (subchip->gpadc_page) {
+ regmap_exit(subchip->regmap_gpadc);
+ i2c_unregister_device(subchip->gpadc_page);
+ }
+}
+
+static int __devinit device_800_init(struct pm80x_chip *chip,
+ struct pm80x_platform_data *pdata)
+{
+ int ret, pmic_id;
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+ goto out;
+ }
+
+ pmic_id = val & PM80X_VERSION_MASK;
+
+ if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) {
+ chip->version = val;
+ dev_info(chip->dev,
+ "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val);
+ } else {
+ dev_err(chip->dev,
+ "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * alarm wake up bit will be clear in device_irq_init(),
+ * read before that
+ */
+ ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
+ goto out;
+ }
+ if (val & PM800_ALARM_WAKEUP) {
+ if (pdata && pdata->rtc)
+ pdata->rtc->rtc_wakeup = 1;
+ }
+
+ ret = device_gpadc_init(chip, pdata);
+ if (ret < 0) {
+ dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
+ goto out;
+ }
+
+ chip->regmap_irq_chip = &pm800_irq_chip;
+
+ ret = device_irq_init_800(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
+ goto out;
+ }
+
+ ret =
+ mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+ ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add onkey subdev\n");
+ goto out_dev;
+ } else
+ dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__);
+
+ if (pdata && pdata->rtc) {
+ rtc_devs[0].platform_data = pdata->rtc;
+ rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
+ ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+ ARRAY_SIZE(rtc_devs), NULL, 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add rtc subdev\n");
+ goto out_dev;
+ } else
+ dev_info(chip->dev,
+ "[%s]:Added mfd rtc_devs\n", __func__);
+ }
+
+ return 0;
+out_dev:
+ mfd_remove_devices(chip->dev);
+ device_irq_exit_800(chip);
+out:
+ return ret;
+}
+
+static int __devinit pm800_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct pm80x_chip *chip;
+ struct pm80x_platform_data *pdata = client->dev.platform_data;
+ struct pm80x_subchip *subchip;
+
+ ret = pm80x_init(client, id);
+ if (ret) {
+ dev_err(&client->dev, "pm800_init fail\n");
+ goto out_init;
+ }
+
+ chip = i2c_get_clientdata(client);
+
+ /* init subchip for PM800 */
+ subchip =
+ devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
+ GFP_KERNEL);
+ if (!subchip) {
+ ret = -ENOMEM;
+ goto err_subchip_alloc;
+ }
+
+ subchip->power_page_addr = pdata->power_page_addr;
+ subchip->gpadc_page_addr = pdata->gpadc_page_addr;
+ chip->subchip = subchip;
+
+ ret = device_800_init(chip, pdata);
+ if (ret) {
+ dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
+ goto err_800_init;
+ }
+
+ ret = pm800_pages_init(chip);
+ if (ret) {
+ dev_err(&client->dev, "pm800_pages_init failed!\n");
+ goto err_page_init;
+ }
+
+ if (pdata->plat_config)
+ pdata->plat_config(chip, pdata);
+
+err_page_init:
+ mfd_remove_devices(chip->dev);
+ device_irq_exit_800(chip);
+err_800_init:
+ devm_kfree(&client->dev, subchip);
+err_subchip_alloc:
+ pm80x_deinit(client);
+out_init:
+ return ret;
+}
+
+static int __devexit pm800_remove(struct i2c_client *client)
+{
+ struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+ mfd_remove_devices(chip->dev);
+ device_irq_exit_800(chip);
+
+ pm800_pages_exit(chip);
+ devm_kfree(&client->dev, chip->subchip);
+
+ pm80x_deinit(client);
+
+ return 0;
+}
+
+static struct i2c_driver pm800_driver = {
+ .driver = {
+ .name = "88PM80X",
+ .owner = THIS_MODULE,
+ .pm = &pm80x_pm_ops,
+ },
+ .probe = pm800_probe,
+ .remove = __devexit_p(pm800_remove),
+ .id_table = pm80x_id_table,
+};
+
+static int __init pm800_i2c_init(void)
+{
+ return i2c_add_driver(&pm800_driver);
+}
+subsys_initcall(pm800_i2c_init);
+
+static void __exit pm800_i2c_exit(void)
+{
+ i2c_del_driver(&pm800_driver);
+}
+module_exit(pm800_i2c_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
new file mode 100644
index 000000000000..6146583589f6
--- /dev/null
+++ b/drivers/mfd/88pm805.c
@@ -0,0 +1,301 @@
+/*
+ * Base driver for Marvell 88PM805
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#define PM805_CHIP_ID (0x00)
+
+static const struct i2c_device_id pm80x_id_table[] = {
+ {"88PM805", CHIP_PM805},
+ {} /* NULL terminated */
+};
+MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
+
+/* Interrupt Number in 88PM805 */
+enum {
+ PM805_IRQ_LDO_OFF, /*0 */
+ PM805_IRQ_SRC_DPLL_LOCK, /*1 */
+ PM805_IRQ_CLIP_FAULT,
+ PM805_IRQ_MIC_CONFLICT,
+ PM805_IRQ_HP2_SHRT,
+ PM805_IRQ_HP1_SHRT, /*5 */
+ PM805_IRQ_FINE_PLL_FAULT,
+ PM805_IRQ_RAW_PLL_FAULT,
+ PM805_IRQ_VOLP_BTN_DET,
+ PM805_IRQ_VOLM_BTN_DET,
+ PM805_IRQ_SHRT_BTN_DET, /*10 */
+ PM805_IRQ_MIC_DET, /*11 */
+
+ PM805_MAX_IRQ,
+};
+
+static struct resource codec_resources[] = {
+ {
+ /* Headset microphone insertion or removal */
+ .name = "micin",
+ .start = PM805_IRQ_MIC_DET,
+ .end = PM805_IRQ_MIC_DET,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ /* Audio short HP1 */
+ .name = "audio-short1",
+ .start = PM805_IRQ_HP1_SHRT,
+ .end = PM805_IRQ_HP1_SHRT,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ /* Audio short HP2 */
+ .name = "audio-short2",
+ .start = PM805_IRQ_HP2_SHRT,
+ .end = PM805_IRQ_HP2_SHRT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell codec_devs[] = {
+ {
+ .name = "88pm80x-codec",
+ .num_resources = ARRAY_SIZE(codec_resources),
+ .resources = &codec_resources[0],
+ .id = -1,
+ },
+};
+
+static struct regmap_irq pm805_irqs[] = {
+ /* INT0 */
+ [PM805_IRQ_LDO_OFF] = {
+ .mask = PM805_INT1_HP1_SHRT,
+ },
+ [PM805_IRQ_SRC_DPLL_LOCK] = {
+ .mask = PM805_INT1_HP2_SHRT,
+ },
+ [PM805_IRQ_CLIP_FAULT] = {
+ .mask = PM805_INT1_MIC_CONFLICT,
+ },
+ [PM805_IRQ_MIC_CONFLICT] = {
+ .mask = PM805_INT1_CLIP_FAULT,
+ },
+ [PM805_IRQ_HP2_SHRT] = {
+ .mask = PM805_INT1_LDO_OFF,
+ },
+ [PM805_IRQ_HP1_SHRT] = {
+ .mask = PM805_INT1_SRC_DPLL_LOCK,
+ },
+ /* INT1 */
+ [PM805_IRQ_FINE_PLL_FAULT] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_MIC_DET,
+ },
+ [PM805_IRQ_RAW_PLL_FAULT] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_SHRT_BTN_DET,
+ },
+ [PM805_IRQ_VOLP_BTN_DET] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_VOLM_BTN_DET,
+ },
+ [PM805_IRQ_VOLM_BTN_DET] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_VOLP_BTN_DET,
+ },
+ [PM805_IRQ_SHRT_BTN_DET] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_RAW_PLL_FAULT,
+ },
+ [PM805_IRQ_MIC_DET] = {
+ .reg_offset = 1,
+ .mask = PM805_INT2_FINE_PLL_FAULT,
+ },
+};
+
+static int __devinit device_irq_init_805(struct pm80x_chip *chip)
+{
+ struct regmap *map = chip->regmap;
+ unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ int data, mask, ret = -EINVAL;
+
+ if (!map || !chip->irq) {
+ dev_err(chip->dev, "incorrect parameters\n");
+ return -EINVAL;
+ }
+
+ /*
+ * irq_mode defines the way of clearing interrupt. it's read-clear by
+ * default.
+ */
+ mask =
+ PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
+ PM800_STATUS0_INT_MASK;
+
+ data = PM805_STATUS0_INT_CLEAR;
+ ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
+ /*
+ * PM805_INT_STATUS is under 32K clock domain, so need to
+ * add proper delay before the next I2C register access.
+ */
+ msleep(1);
+
+ if (ret < 0)
+ goto out;
+
+ ret =
+ regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
+ chip->regmap_irq_chip, &chip->irq_data);
+
+out:
+ return ret;
+}
+
+static void device_irq_exit_805(struct pm80x_chip *chip)
+{
+ regmap_del_irq_chip(chip->irq, chip->irq_data);
+}
+
+static struct regmap_irq_chip pm805_irq_chip = {
+ .name = "88pm805",
+ .irqs = pm805_irqs,
+ .num_irqs = ARRAY_SIZE(pm805_irqs),
+
+ .num_regs = 2,
+ .status_base = PM805_INT_STATUS1,
+ .mask_base = PM805_INT_MASK1,
+ .ack_base = PM805_INT_STATUS1,
+};
+
+static int __devinit device_805_init(struct pm80x_chip *chip)
+{
+ int ret = 0;
+ unsigned int val;
+ struct regmap *map = chip->regmap;
+
+ if (!map) {
+ dev_err(chip->dev, "regmap is invalid\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_read(map, PM805_CHIP_ID, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+ goto out_irq_init;
+ }
+ chip->version = val;
+
+ chip->regmap_irq_chip = &pm805_irq_chip;
+
+ ret = device_irq_init_805(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to init pm805 irq!\n");
+ goto out_irq_init;
+ }
+
+ ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
+ ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add codec subdev\n");
+ goto out_codec;
+ } else
+ dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
+
+ return 0;
+
+out_codec:
+ device_irq_exit_805(chip);
+out_irq_init:
+ return ret;
+}
+
+static int __devinit pm805_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct pm80x_chip *chip;
+ struct pm80x_platform_data *pdata = client->dev.platform_data;
+
+ ret = pm80x_init(client, id);
+ if (ret) {
+ dev_err(&client->dev, "pm805_init fail!\n");
+ goto out_init;
+ }
+
+ chip = i2c_get_clientdata(client);
+
+ ret = device_805_init(chip);
+ if (ret) {
+ dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
+ goto err_805_init;
+ }
+
+ if (pdata->plat_config)
+ pdata->plat_config(chip, pdata);
+
+err_805_init:
+ pm80x_deinit(client);
+out_init:
+ return ret;
+}
+
+static int __devexit pm805_remove(struct i2c_client *client)
+{
+ struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+ mfd_remove_devices(chip->dev);
+ device_irq_exit_805(chip);
+
+ pm80x_deinit(client);
+
+ return 0;
+}
+
+static struct i2c_driver pm805_driver = {
+ .driver = {
+ .name = "88PM80X",
+ .owner = THIS_MODULE,
+ .pm = &pm80x_pm_ops,
+ },
+ .probe = pm805_probe,
+ .remove = __devexit_p(pm805_remove),
+ .id_table = pm80x_id_table,
+};
+
+static int __init pm805_i2c_init(void)
+{
+ return i2c_add_driver(&pm805_driver);
+}
+subsys_initcall(pm805_i2c_init);
+
+static void __exit pm805_i2c_exit(void)
+{
+ i2c_del_driver(&pm805_driver);
+}
+module_exit(pm805_i2c_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
new file mode 100644
index 000000000000..cd0bf527d764
--- /dev/null
+++ b/drivers/mfd/88pm80x.c
@@ -0,0 +1,145 @@
+/*
+ * I2C driver for Marvell 88PM80x
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/err.h>
+
+/*
+ * workaround: some registers needed by pm805 are defined in pm800, so
+ * need to use this global variable to maintain the relation between
+ * pm800 and pm805. would remove it after HW chip fixes the issue.
+ */
+static struct pm80x_chip *g_pm80x_chip;
+
+const struct regmap_config pm80x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+EXPORT_SYMBOL_GPL(pm80x_regmap_config);
+
+int __devinit pm80x_init(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pm80x_chip *chip;
+ struct regmap *map;
+ int ret = 0;
+
+ chip =
+ devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
+ if (IS_ERR(map)) {
+ ret = PTR_ERR(map);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto err_regmap_init;
+ }
+
+ chip->id = id->driver_data;
+ if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
+ ret = -EINVAL;
+ goto err_chip_id;
+ }
+
+ chip->client = client;
+ chip->regmap = map;
+
+ chip->irq = client->irq;
+
+ chip->dev = &client->dev;
+ dev_set_drvdata(chip->dev, chip);
+ i2c_set_clientdata(chip->client, chip);
+
+ device_init_wakeup(&client->dev, 1);
+
+ /*
+ * workaround: set g_pm80x_chip to the first probed chip. if the
+ * second chip is probed, just point to the companion to each
+ * other so that pm805 can access those specific register. would
+ * remove it after HW chip fixes the issue.
+ */
+ if (!g_pm80x_chip)
+ g_pm80x_chip = chip;
+ else {
+ chip->companion = g_pm80x_chip->client;
+ g_pm80x_chip->companion = chip->client;
+ }
+
+ return 0;
+
+err_chip_id:
+ regmap_exit(map);
+err_regmap_init:
+ devm_kfree(&client->dev, chip);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm80x_init);
+
+int pm80x_deinit(struct i2c_client *client)
+{
+ struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+ /*
+ * workaround: clear the dependency between pm800 and pm805.
+ * would remove it after HW chip fixes the issue.
+ */
+ if (g_pm80x_chip->companion)
+ g_pm80x_chip->companion = NULL;
+ else
+ g_pm80x_chip = NULL;
+
+ regmap_exit(chip->regmap);
+ devm_kfree(&client->dev, chip);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm80x_deinit);
+
+#ifdef CONFIG_PM_SLEEP
+static int pm80x_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+ if (chip && chip->wu_flag)
+ if (device_may_wakeup(chip->dev))
+ enable_irq_wake(chip->irq);
+
+ return 0;
+}
+
+static int pm80x_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct pm80x_chip *chip = i2c_get_clientdata(client);
+
+ if (chip && chip->wu_flag)
+ if (device_may_wakeup(chip->dev))
+ disable_irq_wake(chip->irq);
+
+ return 0;
+}
+#endif
+
+SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
+EXPORT_SYMBOL_GPL(pm80x_pm_ops);
+
+MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 87bd5ba38d5b..d09918cf1b15 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = {
{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
};
+static struct resource preg_resources[] __devinitdata = {
+ {PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_IO,},
+};
+
static struct resource rtc_resources[] __devinitdata = {
{PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
};
@@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = {
{"88pm860x-codec", -1,},
};
+static struct regulator_consumer_supply preg_supply[] = {
+ REGULATOR_SUPPLY("preg", "charger-manager"),
+};
+
+static struct regulator_init_data preg_init_data = {
+ .num_consumer_supplies = ARRAY_SIZE(preg_supply),
+ .consumer_supplies = &preg_supply[0],
+};
+
static struct mfd_cell power_devs[] = {
{"88pm860x-battery", -1,},
{"88pm860x-charger", -1,},
+ {"88pm860x-preg", -1,},
};
static struct mfd_cell rtc_devs[] = {
@@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
&charger_resources[0], chip->irq_base);
if (ret < 0)
dev_err(chip->dev, "Failed to add charger subdev\n");
+
+ power_devs[2].platform_data = &preg_init_data;
+ power_devs[2].pdata_size = sizeof(struct regulator_init_data);
+ power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
+ power_devs[2].resources = &preg_resources[0],
+ ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
+ &preg_resources[0], chip->irq_base);
+ if (ret < 0)
+ dev_err(chip->dev, "Failed to add preg subdev\n");
}
static void __devinit device_onkey_init(struct pm860x_chip *chip,
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 92144ed1ad46..d1facef28a60 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -7,6 +7,7 @@ menu "Multifunction device drivers"
config MFD_CORE
tristate
+ select IRQ_DOMAIN
default n
config MFD_88PM860X
@@ -20,6 +21,30 @@ config MFD_88PM860X
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
+config MFD_88PM800
+ tristate "Support Marvell 88PM800"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM800 Power Management IC.
+ This includes the I2C driver and the core APIs _only_, you have to
+ select individual components like voltage regulators, RTC and
+ battery-charger under the corresponding menus.
+
+config MFD_88PM805
+ tristate "Support Marvell 88PM805"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM805 Power Management IC. This includes
+ the I2C driver and the core APIs _only_, you have to select individual
+ components like codec device, headset/Mic device under the
+ corresponding menus.
+
config MFD_SM501
tristate "Support for Silicon Motion SM501"
---help---
@@ -173,8 +198,9 @@ config MFD_TPS65217
config MFD_TPS6586X
bool "TPS6586x Power Management chips"
- depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
+ depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
+ select REGMAP_I2C
depends on REGULATOR
help
If you say yes here you get support for the TPS6586X series of
@@ -276,6 +302,7 @@ config TWL6030_PWM
tristate "TWL6030 PWM (Pulse Width Modulator) Support"
depends on TWL4030_CORE
select HAVE_PWM
+ depends on !PWM
default n
help
Say yes here if you want support for TWL6030 PWM.
@@ -423,6 +450,19 @@ config PMIC_ADP5520
individual components like LCD backlight, LEDs, GPIOs and Kepad
under the corresponding menus.
+config MFD_MAX77686
+ bool "Maxim Semiconductor MAX77686 PMIC Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ select REGMAP_I2C
+ select IRQ_DOMAIN
+ help
+ Say yes here to support for Maxim Semiconductor MAX77686.
+ This is a Power Management IC with RTC on chip.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
config MFD_MAX77693
bool "Maxim Semiconductor MAX77693 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
@@ -450,6 +490,7 @@ config MFD_MAX8997
bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
+ select IRQ_DOMAIN
help
Say yes here to support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
@@ -469,17 +510,56 @@ config MFD_MAX8998
additional drivers must be enabled in order to use the functionality
of the device.
-config MFD_S5M_CORE
- bool "SAMSUNG S5M Series Support"
+config MFD_SEC_CORE
+ bool "SAMSUNG Electronics PMIC Series Support"
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
+ select REGMAP_IRQ
help
- Support for the Samsung Electronics S5M MFD series.
+ Support for the Samsung Electronics MFD series.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
of the device
+config MFD_ARIZONA
+ select REGMAP
+ select REGMAP_IRQ
+ select MFD_CORE
+ bool
+
+config MFD_ARIZONA_I2C
+ tristate "Support Wolfson Microelectronics Arizona platform with I2C"
+ select MFD_ARIZONA
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Support for the Wolfson Microelectronics Arizona platform audio SoC
+ core functionality controlled via I2C.
+
+config MFD_ARIZONA_SPI
+ tristate "Support Wolfson Microelectronics Arizona platform with SPI"
+ select MFD_ARIZONA
+ select MFD_CORE
+ select REGMAP_SPI
+ depends on SPI_MASTER
+ help
+ Support for the Wolfson Microelectronics Arizona platform audio SoC
+ core functionality controlled via I2C.
+
+config MFD_WM5102
+ bool "Support Wolfson Microelectronics WM5102"
+ depends on MFD_ARIZONA
+ help
+ Support for Wolfson Microelectronics WM5102 low power audio SoC
+
+config MFD_WM5110
+ bool "Support Wolfson Microelectronics WM5110"
+ depends on MFD_ARIZONA
+ help
+ Support for Wolfson Microelectronics WM5110 low power audio SoC
+
config MFD_WM8400
bool "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@@ -697,6 +777,7 @@ config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
select MFD_CORE
+ select IRQ_DOMAIN
help
Select this option to enable access to AB8500 power management
chip. This connects to U8500 either on the SSP/SPI bus (deprecated
@@ -704,16 +785,6 @@ config AB8500_CORE
the irq_chip parts for handling the Mixed Signal chip events.
This chip embeds various other multimedia funtionalities as well.
-config AB8500_I2C_CORE
- bool "AB8500 register access via PRCMU I2C"
- depends on AB8500_CORE && MFD_DB8500_PRCMU
- default y
- help
- This enables register access to the AB8500 chip via PRCMU I2C.
- The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
- the I2C bus is connected to the Power Reset
- and Mangagement Unit, PRCMU.
-
config AB8500_DEBUG
bool "Enable debug info via debugfs"
depends on AB8500_CORE && DEBUG_FS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 75f6ed68a4b9..79dd22d1dc3d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -4,6 +4,8 @@
88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
+obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
+obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
@@ -24,6 +26,16 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
+obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
+obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
+obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o
+obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o
+ifneq ($(CONFIG_MFD_WM5102),n)
+obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o
+endif
+ifneq ($(CONFIG_MFD_WM5110),n)
+obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o
+endif
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
wm831x-objs += wm831x-auxadc.o
@@ -78,6 +90,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
+obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
@@ -116,6 +129,6 @@ obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
-obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
+obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 1efad20fb175..78fca2902c8d 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -409,8 +409,6 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
u32 fatevent;
int err;
- add_interrupt_randomness(irq);
-
err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
event_regs, 3);
if (err)
@@ -867,7 +865,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
int err;
int i;
- ab3100 = kzalloc(sizeof(struct ab3100), GFP_KERNEL);
+ ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL);
if (!ab3100) {
dev_err(&client->dev, "could not allocate AB3100 device\n");
return -ENOMEM;
@@ -921,7 +919,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
/* Attach a second dummy i2c_client to the test register address */
ab3100->testreg_client = i2c_new_dummy(client->adapter,
- client->addr + 1);
+ client->addr + 1);
if (!ab3100->testreg_client) {
err = -ENOMEM;
goto exit_no_testreg_client;
@@ -931,11 +929,9 @@ static int __devinit ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_setup;
- err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler,
- IRQF_ONESHOT, "ab3100-core", ab3100);
- /* This real unpredictable IRQ is of course sampled for entropy */
- rand_initialize_irq(client->irq);
-
+ err = devm_request_threaded_irq(&client->dev,
+ client->irq, NULL, ab3100_irq_handler,
+ IRQF_ONESHOT, "ab3100-core", ab3100);
if (err)
goto exit_no_irq;
@@ -962,7 +958,6 @@ static int __devinit ab3100_probe(struct i2c_client *client,
i2c_unregister_device(ab3100->testreg_client);
exit_no_testreg_client:
exit_no_detect:
- kfree(ab3100);
return err;
}
@@ -972,16 +967,8 @@ static int __devexit ab3100_remove(struct i2c_client *client)
/* Unregister subdevices */
mfd_remove_devices(&client->dev);
-
ab3100_remove_debugfs();
i2c_unregister_device(ab3100->testreg_client);
-
- /*
- * At this point, all subscribers should have unregistered
- * their notifiers so deactivate IRQ
- */
- free_irq(client->irq, ab3100);
- kfree(ab3100);
return 0;
}
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index dac0e2998603..626b4ecaf647 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -140,7 +141,7 @@ static const char ab8500_version_str[][7] = {
[AB8500_VERSION_AB8540] = "AB8540",
};
-static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_prcmu_write(struct ab8500 *ab8500, u16 addr, u8 data)
{
int ret;
@@ -150,7 +151,7 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
return ret;
}
-static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+static int ab8500_prcmu_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
u8 data)
{
int ret;
@@ -162,7 +163,7 @@ static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
return ret;
}
-static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+static int ab8500_prcmu_read(struct ab8500 *ab8500, u16 addr)
{
int ret;
u8 data;
@@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
static void ab8500_irq_mask(struct irq_data *data)
{
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
- int offset = data->irq - ab8500->irq_base;
+ int offset = data->hwirq;
int index = offset / 8;
int mask = 1 << (offset % 8);
@@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data)
static void ab8500_irq_unmask(struct irq_data *data)
{
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
- int offset = data->irq - ab8500->irq_base;
+ int offset = data->hwirq;
int index = offset / 8;
int mask = 1 << (offset % 8);
@@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-static int ab8500_irq_init(struct ab8500 *ab8500)
+/**
+ * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
+ *
+ * @ab8500: ab8500_irq controller to operate on.
+ * @irq: index of the interrupt requested in the chip IRQs
+ *
+ * Useful for drivers to request their own IRQs.
+ */
+int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
{
- int base = ab8500->irq_base;
- int irq;
- int num_irqs;
+ if (!ab8500)
+ return -EINVAL;
- if (is_ab9540(ab8500))
- num_irqs = AB9540_NR_IRQS;
- else if (is_ab8505(ab8500))
- num_irqs = AB8505_NR_IRQS;
- else
- num_irqs = AB8500_NR_IRQS;
+ return irq_create_mapping(ab8500->domain, irq);
+}
+EXPORT_SYMBOL_GPL(ab8500_irq_get_virq);
+
+static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct ab8500 *ab8500 = d->host_data;
- for (irq = base; irq < base + num_irqs; irq++) {
- irq_set_chip_data(irq, ab8500);
- irq_set_chip_and_handler(irq, &ab8500_irq_chip,
- handle_simple_irq);
- irq_set_nested_thread(irq, 1);
+ if (!ab8500)
+ return -EINVAL;
+
+ irq_set_chip_data(virq, ab8500);
+ irq_set_chip_and_handler(virq, &ab8500_irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
+ set_irq_flags(virq, IRQF_VALID);
#else
- irq_set_noprobe(irq);
+ irq_set_noprobe(virq);
#endif
- }
return 0;
}
-static void ab8500_irq_remove(struct ab8500 *ab8500)
+static struct irq_domain_ops ab8500_irq_ops = {
+ .map = ab8500_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
{
- int base = ab8500->irq_base;
- int irq;
int num_irqs;
if (is_ab9540(ab8500))
@@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
else
num_irqs = AB8500_NR_IRQS;
- for (irq = base; irq < base + num_irqs; irq++) {
-#ifdef CONFIG_ARM
- set_irq_flags(irq, 0);
-#endif
- irq_set_chip_and_handler(irq, NULL, NULL);
- irq_set_chip_data(irq, NULL);
+ if (ab8500->irq_base) {
+ ab8500->domain = irq_domain_add_legacy(
+ NULL, num_irqs, ab8500->irq_base,
+ 0, &ab8500_irq_ops, ab8500);
+ }
+ else {
+ ab8500->domain = irq_domain_add_linear(
+ np, num_irqs, &ab8500_irq_ops, ab8500);
+ }
+
+ if (!ab8500->domain) {
+ dev_err(ab8500->dev, "Failed to create irqdomain\n");
+ return -ENOSYS;
}
+
+ return 0;
}
int ab8500_suspend(struct ab8500 *ab8500)
@@ -947,54 +970,69 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
+ .of_compatible = "stericsson,ab8500-debug",
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
.resources = ab8500_debug_resources,
},
#endif
{
.name = "ab8500-sysctrl",
+ .of_compatible = "stericsson,ab8500-sysctrl",
},
{
.name = "ab8500-regulator",
+ .of_compatible = "stericsson,ab8500-regulator",
},
{
.name = "ab8500-gpadc",
+ .of_compatible = "stericsson,ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
+ .of_compatible = "stericsson,ab8500-rtc",
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
+ .of_compatible = "stericsson,ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
+ .of_compatible = "stericsson,ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
.resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
+ .of_compatible = "stericsson,ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-pwm",
+ .of_compatible = "stericsson,ab8500-pwm",
.id = 2,
},
{
.name = "ab8500-pwm",
+ .of_compatible = "stericsson,ab8500-pwm",
.id = 3,
},
- { .name = "ab8500-leds", },
+ {
+ .name = "ab8500-leds",
+ .of_compatible = "stericsson,ab8500-leds",
+ },
{
.name = "ab8500-denc",
+ .of_compatible = "stericsson,ab8500-denc",
},
{
.name = "ab8500-temp",
+ .of_compatible = "stericsson,ab8500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
@@ -1026,11 +1064,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
static struct mfd_cell __devinitdata ab8500_devs[] = {
{
.name = "ab8500-gpio",
+ .of_compatible = "stericsson,ab8500-gpio",
.num_resources = ARRAY_SIZE(ab8500_gpio_resources),
.resources = ab8500_gpio_resources,
},
{
.name = "ab8500-usb",
+ .of_compatible = "stericsson,ab8500-usb",
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
.resources = ab8500_usb_resources,
},
@@ -1207,16 +1247,17 @@ static struct attribute_group ab9540_attr_group = {
.attrs = ab9540_sysfs_entries,
};
-static const struct of_device_id ab8500_match[] = {
- {
- .compatible = "stericsson,ab8500",
- .data = (void *)AB8500_VERSION_AB8500,
- },
- {},
-};
-
static int __devinit ab8500_probe(struct platform_device *pdev)
{
+ static char *switch_off_status[] = {
+ "Swoff bit programming",
+ "Thermal protection activation",
+ "Vbat lower then BattOk falling threshold",
+ "Watchdog expired",
+ "Non presence of 32kHz clock",
+ "Battery level lower than power on reset threshold",
+ "Power on key 1 pressed longer than 10 seconds",
+ "DB8500 thermal shutdown"};
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
const struct platform_device_id *platid = platform_get_device_id(pdev);
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@@ -1233,14 +1274,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
if (plat)
ab8500->irq_base = plat->irq_base;
- else if (np)
- ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
-
- if (!ab8500->irq_base) {
- dev_info(&pdev->dev, "couldn't find irq-base\n");
- ret = -EINVAL;
- goto out_free_ab8500;
- }
ab8500->dev = &pdev->dev;
@@ -1252,9 +1285,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
ab8500->irq = resource->start;
- ab8500->read = ab8500_i2c_read;
- ab8500->write = ab8500_i2c_write;
- ab8500->write_masked = ab8500_i2c_write_masked;
+ ab8500->read = ab8500_prcmu_read;
+ ab8500->write = ab8500_prcmu_write;
+ ab8500->write_masked = ab8500_prcmu_write_masked;
mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
@@ -1264,9 +1297,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
if (platid)
version = platid->driver_data;
- else if (np)
- version = (unsigned int)
- of_match_device(ab8500_match, &pdev->dev)->data;
if (version != AB8500_VERSION_UNDEFINED)
ab8500->version = version;
@@ -1323,7 +1353,20 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
AB8500_SWITCH_OFF_STATUS, &value);
if (ret < 0)
return ret;
- dev_info(ab8500->dev, "switch off status: %#x", value);
+ dev_info(ab8500->dev, "switch off cause(s) (%#x): ", value);
+
+ if (value) {
+ for (i = 0; i < ARRAY_SIZE(switch_off_status); i++) {
+ if (value & 1)
+ printk(KERN_CONT " \"%s\"",
+ switch_off_status[i]);
+ value = value >> 1;
+
+ }
+ printk(KERN_CONT "\n");
+ } else {
+ printk(KERN_CONT " None\n");
+ }
if (plat && plat->init)
plat->init(ab8500);
@@ -1352,53 +1395,50 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
for (i = 0; i < ab8500->mask_size; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
- if (ab8500->irq_base) {
- ret = ab8500_irq_init(ab8500);
- if (ret)
- goto out_freeoldmask;
+ ret = ab8500_irq_init(ab8500, np);
+ if (ret)
+ goto out_freeoldmask;
- /* Activate this feature only in ab9540 */
- /* till tests are done on ab8500 1p2 or later*/
- if (is_ab9540(ab8500))
- ret = request_threaded_irq(ab8500->irq, NULL,
+ /* Activate this feature only in ab9540 */
+ /* till tests are done on ab8500 1p2 or later*/
+ if (is_ab9540(ab8500)) {
+ ret = request_threaded_irq(ab8500->irq, NULL,
ab8500_hierarchical_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
- else
- ret = request_threaded_irq(ab8500->irq, NULL,
+ }
+ else {
+ ret = request_threaded_irq(ab8500->irq, NULL,
ab8500_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
"ab8500", ab8500);
if (ret)
- goto out_removeirq;
+ goto out_freeoldmask;
}
- if (!np) {
- ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
- ARRAY_SIZE(abx500_common_devs), NULL,
- ab8500->irq_base);
+ ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+ ARRAY_SIZE(abx500_common_devs), NULL,
+ ab8500->irq_base);
+ if (ret)
+ goto out_freeirq;
- if (ret)
- goto out_freeirq;
-
- if (is_ab9540(ab8500))
- ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
- ARRAY_SIZE(ab9540_devs), NULL,
- ab8500->irq_base);
- else
- ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
- ARRAY_SIZE(ab8500_devs), NULL,
- ab8500->irq_base);
- if (ret)
- goto out_freeirq;
+ if (is_ab9540(ab8500))
+ ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+ ARRAY_SIZE(ab9540_devs), NULL,
+ ab8500->irq_base);
+ else
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+ ARRAY_SIZE(ab8500_devs), NULL,
+ ab8500->irq_base);
+ if (ret)
+ goto out_freeirq;
- if (is_ab9540(ab8500) || is_ab8505(ab8500))
- ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
- ARRAY_SIZE(ab9540_ab8505_devs), NULL,
- ab8500->irq_base);
- if (ret)
- goto out_freeirq;
- }
+ if (is_ab9540(ab8500) || is_ab8505(ab8500))
+ ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
+ ARRAY_SIZE(ab9540_ab8505_devs), NULL,
+ ab8500->irq_base);
+ if (ret)
+ goto out_freeirq;
if (!no_bm) {
/* Add battery management devices */
@@ -1417,15 +1457,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
&ab8500_attr_group);
if (ret)
dev_err(ab8500->dev, "error creating sysfs entries\n");
- else
- return ret;
+
+ return ret;
out_freeirq:
- if (ab8500->irq_base)
- free_irq(ab8500->irq, ab8500);
-out_removeirq:
- if (ab8500->irq_base)
- ab8500_irq_remove(ab8500);
+ free_irq(ab8500->irq, ab8500);
out_freeoldmask:
kfree(ab8500->oldmask);
out_freemask:
@@ -1444,11 +1480,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev)
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
else
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
+
mfd_remove_devices(ab8500->dev);
- if (ab8500->irq_base) {
- free_irq(ab8500->irq, ab8500);
- ab8500_irq_remove(ab8500);
- }
+ free_irq(ab8500->irq, ab8500);
+
kfree(ab8500->oldmask);
kfree(ab8500->mask);
kfree(ab8500);
@@ -1468,7 +1503,6 @@ static struct platform_driver ab8500_core_driver = {
.driver = {
.name = "ab8500-core",
.owner = THIS_MODULE,
- .of_match_table = ab8500_match,
},
.probe = ab8500_probe,
.remove = __devexit_p(ab8500_remove),
@@ -1484,7 +1518,7 @@ static void __exit ab8500_core_exit(void)
{
platform_driver_unregister(&ab8500_core_driver);
}
-arch_initcall(ab8500_core_init);
+core_initcall(ab8500_core_init);
module_exit(ab8500_core_exit);
MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 50c4c89ab220..c4cb806978ac 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -31,12 +31,12 @@ struct ab8500_reg_range {
};
/**
- * struct ab8500_i2c_ranges
+ * struct ab8500_prcmu_ranges
* @num_ranges: the number of ranges in the list
* @bankid: bank identifier
* @range: the list of register ranges
*/
-struct ab8500_i2c_ranges {
+struct ab8500_prcmu_ranges {
u8 num_ranges;
u8 bankid;
const struct ab8500_reg_range *range;
@@ -47,7 +47,7 @@ struct ab8500_i2c_ranges {
#define AB8500_REV_REG 0x80
-static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
+static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
.range = 0,
@@ -608,16 +608,10 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf)
return 0;
}
-static const struct of_device_id ab8500_debug_match[] = {
- { .compatible = "stericsson,ab8500-debug", },
- {}
-};
-
static struct platform_driver ab8500_debug_driver = {
.driver = {
.name = "ab8500-debug",
.owner = THIS_MODULE,
- .of_match_table = ab8500_debug_match,
},
.probe = ab8500_debug_probe,
.remove = __devexit_p(ab8500_debug_remove)
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index b86fd8e1ec3f..866f95960b4b 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -599,7 +599,8 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
/* Register interrupt - SwAdcComplete */
ret = request_threaded_irq(gpadc->irq, NULL,
ab8500_bm_gpswadcconvend_handler,
- IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc);
+ IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
+ "ab8500-gpadc", gpadc);
if (ret < 0) {
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
gpadc->irq);
@@ -648,18 +649,12 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_gpadc_match[] = {
- { .compatible = "stericsson,ab8500-gpadc", },
- {}
-};
-
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = __devexit_p(ab8500_gpadc_remove),
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
- .of_match_table = ab8500_gpadc_match,
},
};
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 5a3e51ccf258..c28d4eb1eff0 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -61,16 +61,10 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_sysctrl_match[] = {
- { .compatible = "stericsson,ab8500-sysctrl", },
- {}
-};
-
static struct platform_driver ab8500_sysctrl_driver = {
.driver = {
.name = "ab8500-sysctrl",
.owner = THIS_MODULE,
- .of_match_table = ab8500_sysctrl_match,
},
.probe = ab8500_sysctrl_probe,
.remove = __devexit_p(ab8500_sysctrl_remove),
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index 8d816cce8322..ea8b9475731d 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -320,7 +320,7 @@ static int __devexit adp5520_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int adp5520_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c
index 6da06341f6c9..5576e07576de 100644
--- a/drivers/mfd/anatop-mfd.c
+++ b/drivers/mfd/anatop-mfd.c
@@ -83,7 +83,7 @@ static int __devinit of_anatop_probe(struct platform_device *pdev)
drvdata->ioreg = ioreg;
spin_lock_init(&drvdata->reglock);
platform_set_drvdata(pdev, drvdata);
- of_platform_populate(np, of_anatop_match, NULL, dev);
+ of_platform_populate(np, NULL, NULL, dev);
return 0;
}
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
new file mode 100644
index 000000000000..c7983e862549
--- /dev/null
+++ b/drivers/mfd/arizona-core.c
@@ -0,0 +1,566 @@
+/*
+ * Arizona core driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+static const char *wm5102_core_supplies[] = {
+ "AVDD",
+ "DBVDD1",
+};
+
+int arizona_clk32k_enable(struct arizona *arizona)
+{
+ int ret = 0;
+
+ mutex_lock(&arizona->clk_lock);
+
+ arizona->clk32k_ref++;
+
+ if (arizona->clk32k_ref == 1)
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+ ARIZONA_CLK_32K_ENA,
+ ARIZONA_CLK_32K_ENA);
+
+ if (ret != 0)
+ arizona->clk32k_ref--;
+
+ mutex_unlock(&arizona->clk_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
+
+int arizona_clk32k_disable(struct arizona *arizona)
+{
+ int ret = 0;
+
+ mutex_lock(&arizona->clk_lock);
+
+ BUG_ON(arizona->clk32k_ref <= 0);
+
+ arizona->clk32k_ref--;
+
+ if (arizona->clk32k_ref == 0)
+ regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+ ARIZONA_CLK_32K_ENA, 0);
+
+ mutex_unlock(&arizona->clk_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
+
+static irqreturn_t arizona_clkgen_err(int irq, void *data)
+{
+ struct arizona *arizona = data;
+
+ dev_err(arizona->dev, "CLKGEN error\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_underclocked(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read underclock status: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF3 underclocked\n");
+ if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF3 underclocked\n");
+ if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF1 underclocked\n");
+ if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "ISRC2 underclocked\n");
+ if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "ISRC1 underclocked\n");
+ if (val & ARIZONA_FX_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "FX underclocked\n");
+ if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "ASRC underclocked\n");
+ if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "DAC underclocked\n");
+ if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "ADC underclocked\n");
+ if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
+ dev_err(arizona->dev, "Mixer underclocked\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_overclocked(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val[2];
+ int ret;
+
+ ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
+ &val[0], 2);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read overclock status: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "PWM overclocked\n");
+ if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "FX core overclocked\n");
+ if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "DAC SYS overclocked\n");
+ if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "DAC WARP overclocked\n");
+ if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ADC overclocked\n");
+ if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "Mixer overclocked\n");
+ if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF3 overclocked\n");
+ if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF2 overclocked\n");
+ if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "AIF1 overclocked\n");
+ if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "Pad control overclocked\n");
+
+ if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
+ if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "Slimbus async overclocked\n");
+ if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "Slimbus sync overclocked\n");
+ if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ASRC async system overclocked\n");
+ if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ASRC async WARP overclocked\n");
+ if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ASRC sync system overclocked\n");
+ if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
+ if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "DSP1 overclocked\n");
+ if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ISRC2 overclocked\n");
+ if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
+ dev_err(arizona->dev, "ISRC1 overclocked\n");
+
+ return IRQ_HANDLED;
+}
+
+static int arizona_wait_for_boot(struct arizona *arizona)
+{
+ unsigned int reg;
+ int ret, i;
+
+ /*
+ * We can't use an interrupt as we need to runtime resume to do so,
+ * we won't race with the interrupt handler as it'll be blocked on
+ * runtime resume.
+ */
+ for (i = 0; i < 5; i++) {
+ msleep(1);
+
+ ret = regmap_read(arizona->regmap,
+ ARIZONA_INTERRUPT_RAW_STATUS_5, &reg);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read boot state: %d\n",
+ ret);
+ continue;
+ }
+
+ if (reg & ARIZONA_BOOT_DONE_STS)
+ break;
+ }
+
+ if (reg & ARIZONA_BOOT_DONE_STS) {
+ regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
+ ARIZONA_BOOT_DONE_STS);
+ } else {
+ dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
+ return -ETIMEDOUT;
+ }
+
+ pm_runtime_mark_last_busy(arizona->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int arizona_runtime_resume(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(arizona->dev, "Leaving AoD mode\n");
+
+ ret = regulator_enable(arizona->dcvdd);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(arizona->regmap, false);
+
+ ret = arizona_wait_for_boot(arizona);
+ if (ret != 0) {
+ regulator_disable(arizona->dcvdd);
+ return ret;
+ }
+
+ regcache_sync(arizona->regmap);
+
+ return 0;
+}
+
+static int arizona_runtime_suspend(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Entering AoD mode\n");
+
+ regulator_disable(arizona->dcvdd);
+ regcache_cache_only(arizona->regmap, true);
+ regcache_mark_dirty(arizona->regmap);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops arizona_pm_ops = {
+ SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
+ arizona_runtime_resume,
+ NULL)
+};
+EXPORT_SYMBOL_GPL(arizona_pm_ops);
+
+static struct mfd_cell early_devs[] = {
+ { .name = "arizona-ldo1" },
+};
+
+static struct mfd_cell wm5102_devs[] = {
+ { .name = "arizona-extcon" },
+ { .name = "arizona-gpio" },
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-pwm" },
+ { .name = "wm5102-codec" },
+};
+
+static struct mfd_cell wm5110_devs[] = {
+ { .name = "arizona-extcon" },
+ { .name = "arizona-gpio" },
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-pwm" },
+ { .name = "wm5110-codec" },
+};
+
+int __devinit arizona_dev_init(struct arizona *arizona)
+{
+ struct device *dev = arizona->dev;
+ const char *type_name;
+ unsigned int reg, val;
+ int ret, i;
+
+ dev_set_drvdata(arizona->dev, arizona);
+ mutex_init(&arizona->clk_lock);
+
+ if (dev_get_platdata(arizona->dev))
+ memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
+ sizeof(arizona->pdata));
+
+ regcache_cache_only(arizona->regmap, true);
+
+ switch (arizona->type) {
+ case WM5102:
+ case WM5110:
+ for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
+ arizona->core_supplies[i].supply
+ = wm5102_core_supplies[i];
+ arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
+ break;
+ default:
+ dev_err(arizona->dev, "Unknown device type %d\n",
+ arizona->type);
+ return -EINVAL;
+ }
+
+ ret = mfd_add_devices(arizona->dev, -1, early_devs,
+ ARRAY_SIZE(early_devs), NULL, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to add early children: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
+ arizona->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n",
+ ret);
+ goto err_early;
+ }
+
+ arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD");
+ if (IS_ERR(arizona->dcvdd)) {
+ ret = PTR_ERR(arizona->dcvdd);
+ dev_err(dev, "Failed to request DCVDD: %d\n", ret);
+ goto err_early;
+ }
+
+ ret = regulator_bulk_enable(arizona->num_core_supplies,
+ arizona->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n",
+ ret);
+ goto err_early;
+ }
+
+ ret = regulator_enable(arizona->dcvdd);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
+ goto err_enable;
+ }
+
+ if (arizona->pdata.reset) {
+ /* Start out with /RESET low to put the chip into reset */
+ ret = gpio_request_one(arizona->pdata.reset,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+ "arizona /RESET");
+ if (ret != 0) {
+ dev_err(dev, "Failed to request /RESET: %d\n", ret);
+ goto err_dcvdd;
+ }
+
+ gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ }
+
+ regcache_cache_only(arizona->regmap, false);
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
+ if (ret != 0) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
+ &arizona->rev);
+ if (ret != 0) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ goto err_reset;
+ }
+ arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
+
+ switch (reg) {
+#ifdef CONFIG_MFD_WM5102
+ case 0x5102:
+ type_name = "WM5102";
+ if (arizona->type != WM5102) {
+ dev_err(arizona->dev, "WM5102 registered as %d\n",
+ arizona->type);
+ arizona->type = WM5102;
+ }
+ ret = wm5102_patch(arizona);
+ break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+ case 0x5110:
+ type_name = "WM5110";
+ if (arizona->type != WM5110) {
+ dev_err(arizona->dev, "WM5110 registered as %d\n",
+ arizona->type);
+ arizona->type = WM5110;
+ }
+ ret = wm5110_patch(arizona);
+ break;
+#endif
+ default:
+ dev_err(arizona->dev, "Unknown device ID %x\n", reg);
+ goto err_reset;
+ }
+
+ dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
+
+ if (ret != 0)
+ dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
+
+ /* If we have a /RESET GPIO we'll already be reset */
+ if (!arizona->pdata.reset) {
+ ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
+ if (ret != 0) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_reset;
+ }
+ }
+
+ ret = arizona_wait_for_boot(arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+ if (!arizona->pdata.gpio_defaults[i])
+ continue;
+
+ regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
+ arizona->pdata.gpio_defaults[i]);
+ }
+
+ pm_runtime_set_autosuspend_delay(arizona->dev, 100);
+ pm_runtime_use_autosuspend(arizona->dev);
+ pm_runtime_enable(arizona->dev);
+
+ /* Chip default */
+ if (!arizona->pdata.clk32k_src)
+ arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
+
+ switch (arizona->pdata.clk32k_src) {
+ case ARIZONA_32KZ_MCLK1:
+ case ARIZONA_32KZ_MCLK2:
+ regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+ ARIZONA_CLK_32K_SRC_MASK,
+ arizona->pdata.clk32k_src - 1);
+ break;
+ case ARIZONA_32KZ_NONE:
+ regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
+ ARIZONA_CLK_32K_SRC_MASK, 2);
+ break;
+ default:
+ dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
+ arizona->pdata.clk32k_src);
+ ret = -EINVAL;
+ goto err_reset;
+ }
+
+ for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
+ /* Default for both is 0 so noop with defaults */
+ val = arizona->pdata.dmic_ref[i]
+ << ARIZONA_IN1_DMIC_SUP_SHIFT;
+ val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_IN1L_CONTROL + (i * 8),
+ ARIZONA_IN1_DMIC_SUP_MASK |
+ ARIZONA_IN1_MODE_MASK, val);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
+ /* Default is 0 so noop with defaults */
+ if (arizona->pdata.out_mono[i])
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
+ ARIZONA_OUT1_MONO, val);
+ }
+
+ for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
+ if (arizona->pdata.spk_mute[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
+ ARIZONA_SPK1_MUTE_ENDIAN_MASK |
+ ARIZONA_SPK1_MUTE_SEQ1_MASK,
+ arizona->pdata.spk_mute[i]);
+
+ if (arizona->pdata.spk_fmt[i])
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
+ ARIZONA_SPK1_FMT_MASK,
+ arizona->pdata.spk_fmt[i]);
+ }
+
+ /* Set up for interrupts */
+ ret = arizona_irq_init(arizona);
+ if (ret != 0)
+ goto err_reset;
+
+ arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
+ arizona_clkgen_err, arizona);
+ arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
+ arizona_overclocked, arizona);
+ arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
+ arizona_underclocked, arizona);
+
+ switch (arizona->type) {
+ case WM5102:
+ ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
+ ARRAY_SIZE(wm5102_devs), NULL, 0);
+ break;
+ case WM5110:
+ ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
+ ARRAY_SIZE(wm5102_devs), NULL, 0);
+ break;
+ }
+
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
+ goto err_irq;
+ }
+
+#ifdef CONFIG_PM_RUNTIME
+ regulator_disable(arizona->dcvdd);
+#endif
+
+ return 0;
+
+err_irq:
+ arizona_irq_exit(arizona);
+err_reset:
+ if (arizona->pdata.reset) {
+ gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ gpio_free(arizona->pdata.reset);
+ }
+err_dcvdd:
+ regulator_disable(arizona->dcvdd);
+err_enable:
+ regulator_bulk_disable(arizona->num_core_supplies,
+ arizona->core_supplies);
+err_early:
+ mfd_remove_devices(dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dev_init);
+
+int __devexit arizona_dev_exit(struct arizona *arizona)
+{
+ mfd_remove_devices(arizona->dev);
+ arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
+ arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
+ pm_runtime_disable(arizona->dev);
+ arizona_irq_exit(arizona);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_dev_exit);
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
new file mode 100644
index 000000000000..570c4b438086
--- /dev/null
+++ b/drivers/mfd/arizona-i2c.c
@@ -0,0 +1,97 @@
+/*
+ * Arizona-i2c.c -- Arizona I2C bus interface
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+
+#include "arizona.h"
+
+static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct arizona *arizona;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ switch (id->driver_data) {
+#ifdef CONFIG_MFD_WM5102
+ case WM5102:
+ regmap_config = &wm5102_i2c_regmap;
+ break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+ case WM5110:
+ regmap_config = &wm5110_i2c_regmap;
+ break;
+#endif
+ default:
+ dev_err(&i2c->dev, "Unknown device type %ld\n",
+ id->driver_data);
+ return -EINVAL;
+ }
+
+ arizona = devm_kzalloc(&i2c->dev, sizeof(*arizona), GFP_KERNEL);
+ if (arizona == NULL)
+ return -ENOMEM;
+
+ arizona->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(arizona->regmap)) {
+ ret = PTR_ERR(arizona->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ arizona->type = id->driver_data;
+ arizona->dev = &i2c->dev;
+ arizona->irq = i2c->irq;
+
+ return arizona_dev_init(arizona);
+}
+
+static int __devexit arizona_i2c_remove(struct i2c_client *i2c)
+{
+ struct arizona *arizona = dev_get_drvdata(&i2c->dev);
+ arizona_dev_exit(arizona);
+ return 0;
+}
+
+static const struct i2c_device_id arizona_i2c_id[] = {
+ { "wm5102", WM5102 },
+ { "wm5110", WM5110 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, arizona_i2c_id);
+
+static struct i2c_driver arizona_i2c_driver = {
+ .driver = {
+ .name = "arizona",
+ .owner = THIS_MODULE,
+ .pm = &arizona_pm_ops,
+ },
+ .probe = arizona_i2c_probe,
+ .remove = __devexit_p(arizona_i2c_remove),
+ .id_table = arizona_i2c_id,
+};
+
+module_i2c_driver(arizona_i2c_driver);
+
+MODULE_DESCRIPTION("Arizona I2C bus interface");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
new file mode 100644
index 000000000000..98ac345f468e
--- /dev/null
+++ b/drivers/mfd/arizona-irq.c
@@ -0,0 +1,275 @@
+/*
+ * Arizona interrupt support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+static int arizona_map_irq(struct arizona *arizona, int irq)
+{
+ int ret;
+
+ ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
+ if (ret < 0)
+ ret = regmap_irq_get_virq(arizona->irq_chip, irq);
+
+ return ret;
+}
+
+int arizona_request_irq(struct arizona *arizona, int irq, char *name,
+ irq_handler_t handler, void *data)
+{
+ irq = arizona_map_irq(arizona, irq);
+ if (irq < 0)
+ return irq;
+
+ return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT,
+ name, data);
+}
+EXPORT_SYMBOL_GPL(arizona_request_irq);
+
+void arizona_free_irq(struct arizona *arizona, int irq, void *data)
+{
+ irq = arizona_map_irq(arizona, irq);
+ if (irq < 0)
+ return;
+
+ free_irq(irq, data);
+}
+EXPORT_SYMBOL_GPL(arizona_free_irq);
+
+int arizona_set_irq_wake(struct arizona *arizona, int irq, int on)
+{
+ irq = arizona_map_irq(arizona, irq);
+ if (irq < 0)
+ return irq;
+
+ return irq_set_irq_wake(irq, on);
+}
+EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
+
+static irqreturn_t arizona_boot_done(int irq, void *data)
+{
+ struct arizona *arizona = data;
+
+ dev_dbg(arizona->dev, "Boot done\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_ctrlif_err(int irq, void *data)
+{
+ struct arizona *arizona = data;
+
+ /*
+ * For pretty much all potential sources a register cache sync
+ * won't help, we've just got a software bug somewhere.
+ */
+ dev_err(arizona->dev, "Control interface error\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_irq_thread(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ int i, ret;
+
+ ret = pm_runtime_get_sync(arizona->dev);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* Check both domains */
+ for (i = 0; i < 2; i++)
+ handle_nested_irq(irq_find_mapping(arizona->virq, i));
+
+ pm_runtime_mark_last_busy(arizona->dev);
+ pm_runtime_put_autosuspend(arizona->dev);
+
+ return IRQ_HANDLED;
+}
+
+static void arizona_irq_enable(struct irq_data *data)
+{
+}
+
+static void arizona_irq_disable(struct irq_data *data)
+{
+}
+
+static struct irq_chip arizona_irq_chip = {
+ .name = "arizona",
+ .irq_disable = arizona_irq_disable,
+ .irq_enable = arizona_irq_enable,
+};
+
+static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct regmap_irq_chip_data *data = h->host_data;
+
+ irq_set_chip_data(virq, data);
+ irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+
+ return 0;
+}
+
+static struct irq_domain_ops arizona_domain_ops = {
+ .map = arizona_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+int arizona_irq_init(struct arizona *arizona)
+{
+ int flags = IRQF_ONESHOT;
+ int ret, i;
+ const struct regmap_irq_chip *aod, *irq;
+
+ switch (arizona->type) {
+#ifdef CONFIG_MFD_WM5102
+ case WM5102:
+ aod = &wm5102_aod;
+ irq = &wm5102_irq;
+ break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+ case WM5110:
+ aod = &wm5110_aod;
+ irq = &wm5110_irq;
+ break;
+#endif
+ default:
+ BUG_ON("Unknown Arizona class device" == NULL);
+ return -EINVAL;
+ }
+
+ if (arizona->pdata.irq_active_high) {
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
+ ARIZONA_IRQ_POL, 0);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n",
+ ret);
+ goto err;
+ }
+
+ flags |= IRQF_TRIGGER_HIGH;
+ } else {
+ flags |= IRQF_TRIGGER_LOW;
+ }
+
+ /* Allocate a virtual IRQ domain to distribute to the regmap domains */
+ arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
+ arizona);
+ if (!arizona->virq) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_add_irq_chip(arizona->regmap,
+ irq_create_mapping(arizona->virq, 0),
+ IRQF_ONESHOT, -1, aod,
+ &arizona->aod_irq_chip);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
+ goto err_domain;
+ }
+
+ ret = regmap_add_irq_chip(arizona->regmap,
+ irq_create_mapping(arizona->virq, 1),
+ IRQF_ONESHOT, -1, irq,
+ &arizona->irq_chip);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
+ goto err_aod;
+ }
+
+ /* Make sure the boot done IRQ is unmasked for resumes */
+ i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
+ ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
+ "Boot done", arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
+ arizona->irq, ret);
+ goto err_boot_done;
+ }
+
+ /* Handle control interface errors in the core */
+ i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
+ ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT,
+ "Control interface error", arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
+ arizona->irq, ret);
+ goto err_ctrlif;
+ }
+
+ ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
+ flags, "arizona", arizona);
+
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request IRQ %d: %d\n",
+ arizona->irq, ret);
+ goto err_main_irq;
+ }
+
+ return 0;
+
+err_main_irq:
+ free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
+err_ctrlif:
+ free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
+err_boot_done:
+ regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
+ arizona->irq_chip);
+err_aod:
+ regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
+ arizona->aod_irq_chip);
+err_domain:
+err:
+ return ret;
+}
+
+int arizona_irq_exit(struct arizona *arizona)
+{
+ free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
+ free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
+ regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
+ arizona->irq_chip);
+ regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
+ arizona->aod_irq_chip);
+ free_irq(arizona->irq, arizona);
+
+ return 0;
+}
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
new file mode 100644
index 000000000000..df2e5a8bee28
--- /dev/null
+++ b/drivers/mfd/arizona-spi.c
@@ -0,0 +1,97 @@
+/*
+ * arizona-spi.c -- Arizona SPI bus interface
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/mfd/arizona/core.h>
+
+#include "arizona.h"
+
+static int __devinit arizona_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct arizona *arizona;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ switch (id->driver_data) {
+#ifdef CONFIG_MFD_WM5102
+ case WM5102:
+ regmap_config = &wm5102_spi_regmap;
+ break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+ case WM5110:
+ regmap_config = &wm5110_spi_regmap;
+ break;
+#endif
+ default:
+ dev_err(&spi->dev, "Unknown device type %ld\n",
+ id->driver_data);
+ return -EINVAL;
+ }
+
+ arizona = devm_kzalloc(&spi->dev, sizeof(*arizona), GFP_KERNEL);
+ if (arizona == NULL)
+ return -ENOMEM;
+
+ arizona->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(arizona->regmap)) {
+ ret = PTR_ERR(arizona->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ arizona->type = id->driver_data;
+ arizona->dev = &spi->dev;
+ arizona->irq = spi->irq;
+
+ return arizona_dev_init(arizona);
+}
+
+static int __devexit arizona_spi_remove(struct spi_device *spi)
+{
+ struct arizona *arizona = dev_get_drvdata(&spi->dev);
+ arizona_dev_exit(arizona);
+ return 0;
+}
+
+static const struct spi_device_id arizona_spi_ids[] = {
+ { "wm5102", WM5102 },
+ { "wm5110", WM5110 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, arizona_spi_ids);
+
+static struct spi_driver arizona_spi_driver = {
+ .driver = {
+ .name = "arizona",
+ .owner = THIS_MODULE,
+ .pm = &arizona_pm_ops,
+ },
+ .probe = arizona_spi_probe,
+ .remove = __devexit_p(arizona_spi_remove),
+ .id_table = arizona_spi_ids,
+};
+
+module_spi_driver(arizona_spi_driver);
+
+MODULE_DESCRIPTION("Arizona SPI bus interface");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
new file mode 100644
index 000000000000..9798ae5da67b
--- /dev/null
+++ b/drivers/mfd/arizona.h
@@ -0,0 +1,40 @@
+/*
+ * wm5102.h -- WM5102 MFD internals
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#ifndef _WM5102_H
+#define _WM5102_H
+
+#include <linux/regmap.h>
+#include <linux/pm.h>
+
+struct wm_arizona;
+
+extern const struct regmap_config wm5102_i2c_regmap;
+extern const struct regmap_config wm5102_spi_regmap;
+
+extern const struct regmap_config wm5110_i2c_regmap;
+extern const struct regmap_config wm5110_spi_regmap;
+
+extern const struct dev_pm_ops arizona_pm_ops;
+
+extern const struct regmap_irq_chip wm5102_aod;
+extern const struct regmap_irq_chip wm5102_irq;
+
+extern const struct regmap_irq_chip wm5110_aod;
+extern const struct regmap_irq_chip wm5110_irq;
+
+int arizona_dev_init(struct arizona *arizona);
+int arizona_dev_exit(struct arizona *arizona);
+int arizona_irq_init(struct arizona *arizona);
+int arizona_irq_exit(struct arizona *arizona);
+
+#endif
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 1f1313c90573..2544910e1fd6 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -772,7 +772,6 @@ EXPORT_SYMBOL_GPL(da9052_regmap_config);
int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
{
struct da9052_pdata *pdata = da9052->dev->platform_data;
- struct irq_desc *desc;
int ret;
mutex_init(&da9052->auxadc_lock);
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 50e83dc5dc49..7040a0081130 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
#include <asm/hardware/gic.h>
@@ -2269,10 +2270,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
/**
* prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
*/
-void prcmu_ac_wake_req(void)
+int prcmu_ac_wake_req(void)
{
u32 val;
- u32 status;
+ int ret = 0;
mutex_lock(&mb0_transfer.ac_wake_lock);
@@ -2282,39 +2283,32 @@ void prcmu_ac_wake_req(void)
atomic_set(&ac_wake_req_state, 1);
-retry:
- writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
+ /*
+ * Force Modem Wake-up before hostaccess_req ping-pong.
+ * It prevents Modem to enter in Sleep while acking the hostaccess
+ * request. The 31us delay has been calculated by HWI.
+ */
+ val |= PRCM_HOSTACCESS_REQ_WAKE_REQ;
+ writel(val, PRCM_HOSTACCESS_REQ);
+
+ udelay(31);
+
+ val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ;
+ writel(val, PRCM_HOSTACCESS_REQ);
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
+#if defined(CONFIG_DBX500_PRCMU_DEBUG)
+ db8500_prcmu_debug_dump(__func__, true, true);
+#endif
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
- goto unlock_and_return;
- }
-
- /*
- * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
- * As a workaround, we wait, and then check that the modem is indeed
- * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
- * register, which may not be the whole truth).
- */
- udelay(400);
- status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
- if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
- PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
- pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
- __func__, status);
- udelay(1200);
- writel(val, PRCM_HOSTACCESS_REQ);
- if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
- msecs_to_jiffies(5000)))
- goto retry;
- pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
- __func__);
+ ret = -EFAULT;
}
unlock_and_return:
mutex_unlock(&mb0_transfer.ac_wake_lock);
+ return ret;
}
/**
@@ -2945,14 +2939,31 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
};
+static struct resource ab8500_resources[] = {
+ [0] = {
+ .start = IRQ_DB8500_AB8500,
+ .end = IRQ_DB8500_AB8500,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
static struct mfd_cell db8500_prcmu_devs[] = {
{
.name = "db8500-prcmu-regulators",
+ .of_compatible = "stericsson,db8500-prcmu-regulator",
.platform_data = &db8500_regulators,
.pdata_size = sizeof(db8500_regulators),
},
{
.name = "cpufreq-u8500",
+ .of_compatible = "stericsson,cpufreq-u8500",
+ },
+ {
+ .name = "ab8500-core",
+ .of_compatible = "stericsson,ab8500",
+ .num_resources = ARRAY_SIZE(ab8500_resources),
+ .resources = ab8500_resources,
+ .id = AB8500_VERSION_AB8500,
},
};
@@ -2962,8 +2973,9 @@ static struct mfd_cell db8500_prcmu_devs[] = {
*/
static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
{
+ struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data;
struct device_node *np = pdev->dev.of_node;
- int irq = 0, err = 0;
+ int irq = 0, err = 0, i;
if (ux500_is_svp())
return -ENODEV;
@@ -2987,16 +2999,21 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
+ for (i = 0; i < ARRAY_SIZE(db8500_prcmu_devs); i++) {
+ if (!strcmp(db8500_prcmu_devs[i].name, "ab8500-core")) {
+ db8500_prcmu_devs[i].platform_data = ab8500_platdata;
+ db8500_prcmu_devs[i].pdata_size = sizeof(struct ab8500_platform_data);
+ }
+ }
+
if (cpu_is_u8500v20_or_later())
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
- if (!np) {
- err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
- ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
- if (err) {
- pr_err("prcmu: Failed to add subdevices\n");
- return err;
- }
+ err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
+ ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
+ if (err) {
+ pr_err("prcmu: Failed to add subdevices\n");
+ return err;
}
pr_info("DB8500 PRCMU initialized\n");
@@ -3004,11 +3021,16 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
no_irq_return:
return err;
}
+static const struct of_device_id db8500_prcmu_match[] = {
+ { .compatible = "stericsson,db8500-prcmu"},
+ { },
+};
static struct platform_driver db8500_prcmu_driver = {
.driver = {
.name = "db8500-prcmu",
.owner = THIS_MODULE,
+ .of_match_table = db8500_prcmu_match,
},
.probe = db8500_prcmu_probe,
};
@@ -3018,7 +3040,7 @@ static int __init db8500_prcmu_init(void)
return platform_driver_register(&db8500_prcmu_driver);
}
-arch_initcall(db8500_prcmu_init);
+core_initcall(db8500_prcmu_init);
MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 3a0bf91d7780..23108a6e3167 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -106,6 +106,7 @@
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
+#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16)
#define ARM_WAKEUP_MODEM 0x1
#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 000000000000..cdc3280e2ec7
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,319 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <woong.byun@samsung.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+enum {
+ MAX77686_DEBUG_IRQ_INFO = 1 << 0,
+ MAX77686_DEBUG_IRQ_MASK = 1 << 1,
+ MAX77686_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask = 0;
+module_param(debug_mask, int, 0);
+MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)");
+
+static const u8 max77686_mask_reg[] = {
+ [PMIC_INT1] = MAX77686_REG_INT1MSK,
+ [PMIC_INT2] = MAX77686_REG_INT2MSK,
+ [RTC_INT] = MAX77686_RTC_INTM,
+};
+
+static struct regmap *max77686_get_regmap(struct max77686_dev *max77686,
+ enum max77686_irq_source src)
+{
+ switch (src) {
+ case PMIC_INT1 ... PMIC_INT2:
+ return max77686->regmap;
+ case RTC_INT:
+ return max77686->rtc_regmap;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+struct max77686_irq_data {
+ int mask;
+ enum max77686_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask) \
+ [(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77686_irq_data max77686_irqs[] = {
+ DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0),
+ DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1),
+ DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2),
+ DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6),
+ DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
+ DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
+ DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
+ DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
+ DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
+};
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_info("%s\n", __func__);
+
+ mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ int i;
+
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ u8 mask_reg = max77686_mask_reg[i];
+ struct regmap *map = max77686_get_regmap(max77686, i);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+ __func__, i, mask_reg, max77686->irq_masks_cur[i]);
+
+ if (mask_reg == MAX77686_REG_INVALID ||
+ IS_ERR_OR_NULL(map))
+ continue;
+
+ max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+ regmap_write(map, max77686_mask_reg[i],
+ max77686->irq_masks_cur[i]);
+ }
+
+ mutex_unlock(&max77686->irqlock);
+}
+
+static const inline struct max77686_irq_data *to_max77686_irq(int irq)
+{
+ struct irq_data *data = irq_get_irq_data(irq);
+ return &max77686_irqs[data->hwirq];
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
+
+ max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_info("%s: group=%d, cur=0x%x\n",
+ __func__, irq_data->group,
+ max77686->irq_masks_cur[irq_data->group]);
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
+
+ max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_info("%s: group=%d, cur=0x%x\n",
+ __func__, irq_data->group,
+ max77686->irq_masks_cur[irq_data->group]);
+}
+
+static struct irq_chip max77686_irq_chip = {
+ .name = "max77686",
+ .irq_bus_lock = max77686_irq_lock,
+ .irq_bus_sync_unlock = max77686_irq_sync_unlock,
+ .irq_mask = max77686_irq_mask,
+ .irq_unmask = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+ struct max77686_dev *max77686 = data;
+ unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {};
+ unsigned int irq_src;
+ int ret;
+ int i, cur_irq;
+
+ ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+ if (irq_src == MAX77686_IRQSRC_PMIC) {
+ ret = regmap_bulk_read(max77686->regmap,
+ MAX77686_REG_INT1, irq_reg, 2);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: int1=0x%x, int2=0x%x\n", __func__,
+ irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+ }
+
+ if (irq_src & MAX77686_IRQSRC_RTC) {
+ ret = regmap_read(max77686->rtc_regmap,
+ MAX77686_RTC_INT, &irq_reg[RTC_INT]);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: rtc int=0x%x\n", __func__,
+ irq_reg[RTC_INT]);
+
+ }
+
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++)
+ irq_reg[i] &= ~max77686->irq_masks_cur[i];
+
+ for (i = 0; i < MAX77686_IRQ_NR; i++) {
+ if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
+ cur_irq = irq_find_mapping(max77686->irq_domain, i);
+ if (cur_irq)
+ handle_nested_irq(cur_irq);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct max77686_dev *max77686 = d->host_data;
+
+ irq_set_chip_data(irq, max77686);
+ irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ return 0;
+}
+
+static struct irq_domain_ops max77686_irq_domain_ops = {
+ .map = max77686_irq_domain_map,
+};
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+ struct irq_domain *domain;
+ int i;
+ int ret;
+ int val;
+ struct regmap *map;
+
+ mutex_init(&max77686->irqlock);
+
+ if (max77686->irq_gpio && !max77686->irq) {
+ max77686->irq = gpio_to_irq(max77686->irq_gpio);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT) {
+ ret = gpio_request(max77686->irq_gpio, "pmic_irq");
+ if (ret < 0) {
+ dev_err(max77686->dev,
+ "Failed to request gpio %d with ret:"
+ "%d\n", max77686->irq_gpio, ret);
+ return IRQ_NONE;
+ }
+
+ gpio_direction_input(max77686->irq_gpio);
+ val = gpio_get_value(max77686->irq_gpio);
+ gpio_free(max77686->irq_gpio);
+ pr_info("%s: gpio_irq=%x\n", __func__, val);
+ }
+ }
+
+ if (!max77686->irq) {
+ dev_err(max77686->dev, "irq is not specified\n");
+ return -ENODEV;
+ }
+
+ /* Mask individual interrupt sources */
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ max77686->irq_masks_cur[i] = 0xff;
+ max77686->irq_masks_cache[i] = 0xff;
+ map = max77686_get_regmap(max77686, i);
+
+ if (IS_ERR_OR_NULL(map))
+ continue;
+ if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+ continue;
+
+ regmap_write(map, max77686_mask_reg[i], 0xff);
+ }
+ domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
+ &max77686_irq_domain_ops, max77686);
+ if (!domain) {
+ dev_err(max77686->dev, "could not create irq domain\n");
+ return -ENODEV;
+ }
+ max77686->irq_domain = domain;
+
+ ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max77686-irq", max77686);
+
+ if (ret)
+ dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+ max77686->irq, ret);
+
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+ pr_info("%s-\n", __func__);
+
+ return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+ if (max77686->irq)
+ free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644
index 000000000000..c03e12b51924
--- /dev/null
+++ b/drivers/mfd/max77686.c
@@ -0,0 +1,187 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ * Jonghwa Lee <jonghwa3.lee@samsung.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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/err.h>
+
+#define I2C_ADDR_RTC (0x0C >> 1)
+
+static struct mfd_cell max77686_devs[] = {
+ { .name = "max77686-pmic", },
+ { .name = "max77686-rtc", },
+};
+
+static struct regmap_config max77686_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
+ {.compatible = "maxim,max77686", .data = 0},
+ {},
+};
+
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+ *dev)
+{
+ struct max77686_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return NULL;
+ }
+
+ dev->platform_data = pd;
+ return pd;
+}
+#else
+static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
+ *dev)
+{
+ return 0;
+}
+#endif
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max77686_dev *max77686 = NULL;
+ struct max77686_platform_data *pdata = i2c->dev.platform_data;
+ unsigned int data;
+ int ret = 0;
+
+ if (i2c->dev.of_node)
+ pdata = max77686_i2c_parse_dt_pdata(&i2c->dev);
+
+ if (!pdata) {
+ ret = -EIO;
+ dev_err(&i2c->dev, "No platform data found.\n");
+ goto err;
+ }
+
+ max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+ if (max77686 == NULL)
+ return -ENOMEM;
+
+ max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
+ if (IS_ERR(max77686->regmap)) {
+ ret = PTR_ERR(max77686->regmap);
+ dev_err(max77686->dev, "Failed to allocate register map: %d\n",
+ ret);
+ kfree(max77686);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, max77686);
+ max77686->dev = &i2c->dev;
+ max77686->i2c = i2c;
+ max77686->type = id->driver_data;
+
+ max77686->wakeup = pdata->wakeup;
+ max77686->irq_gpio = pdata->irq_gpio;
+ max77686->irq = i2c->irq;
+
+ if (regmap_read(max77686->regmap,
+ MAX77686_REG_DEVICE_ID, &data) < 0) {
+ dev_err(max77686->dev,
+ "device not found on this channel (this is not an error)\n");
+ ret = -ENODEV;
+ goto err;
+ } else
+ dev_info(max77686->dev, "device found\n");
+
+ max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ i2c_set_clientdata(max77686->rtc, max77686);
+
+ max77686_irq_init(max77686);
+
+ ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+ ARRAY_SIZE(max77686_devs), NULL, 0);
+
+ if (ret < 0)
+ goto err_mfd;
+
+ return ret;
+
+err_mfd:
+ mfd_remove_devices(max77686->dev);
+ i2c_unregister_device(max77686->rtc);
+err:
+ kfree(max77686);
+ return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max77686->dev);
+ i2c_unregister_device(max77686->rtc);
+ kfree(max77686);
+
+ return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+ { "max77686", TYPE_MAX77686 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+static struct i2c_driver max77686_i2c_driver = {
+ .driver = {
+ .name = "max77686",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max77686_pmic_dt_match),
+ },
+ .probe = max77686_i2c_probe,
+ .remove = max77686_i2c_remove,
+ .id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+ return i2c_add_driver(&max77686_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77686_i2c_init);
+
+static void __exit max77686_i2c_exit(void)
+{
+ i2c_del_driver(&max77686_i2c_driver);
+}
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index e9e4278722f3..a1811cb50ec7 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
max77693->wakeup = pdata->wakeup;
- mutex_init(&max77693->iolock);
-
if (max77693_read_reg(max77693->regmap,
MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
dev_err(max77693->dev, "device not found on this channel\n");
@@ -156,7 +154,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
ret = max77693_irq_init(max77693);
if (ret < 0)
- goto err_mfd;
+ goto err_irq;
pm_runtime_set_active(max77693->dev);
@@ -170,11 +168,11 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
return ret;
err_mfd:
+ max77693_irq_exit(max77693);
+err_irq:
i2c_unregister_device(max77693->muic);
i2c_unregister_device(max77693->haptic);
err_regmap:
- kfree(max77693);
-
return ret;
}
@@ -183,6 +181,7 @@ static int max77693_i2c_remove(struct i2c_client *i2c)
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
mfd_remove_devices(max77693->dev);
+ max77693_irq_exit(max77693);
i2c_unregister_device(max77693->muic);
i2c_unregister_device(max77693->haptic);
@@ -215,7 +214,7 @@ static int max77693_resume(struct device *dev)
return max77693_irq_resume(max77693);
}
-const struct dev_pm_ops max77693_pm = {
+static const struct dev_pm_ops max77693_pm = {
.suspend = max77693_suspend,
.resume = max77693_resume,
};
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index ca881efedf75..825a7f06d9ba 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -75,9 +75,9 @@ static struct mfd_cell power_devs[] = {
static struct resource rtc_resources[] = {
{
.name = "max8925-rtc",
- .start = MAX8925_RTC_IRQ,
- .end = MAX8925_RTC_IRQ_MASK,
- .flags = IORESOURCE_IO,
+ .start = MAX8925_IRQ_RTC_ALARM0,
+ .end = MAX8925_IRQ_RTC_ALARM0,
+ .flags = IORESOURCE_IRQ,
},
};
@@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
- &rtc_resources[0], 0);
+ &rtc_resources[0], chip->irq_base);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 09274cf7c33b..43fa61413e93 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
static const inline struct max8997_irq_data *
irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
{
- return &max8997_irqs[irq - max8997->irq_base];
+ struct irq_data *data = irq_get_irq_data(irq);
+ return &max8997_irqs[data->hwirq];
}
static void max8997_irq_mask(struct irq_data *data)
@@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
u8 irq_src;
int ret;
- int i;
+ int i, cur_irq;
ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
if (ret < 0) {
@@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
/* Report */
for (i = 0; i < MAX8997_IRQ_NR; i++) {
- if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask)
- handle_nested_irq(max8997->irq_base + i);
+ if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) {
+ cur_irq = irq_find_mapping(max8997->irq_domain, i);
+ if (cur_irq)
+ handle_nested_irq(cur_irq);
+ }
}
return IRQ_HANDLED;
@@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
int max8997_irq_resume(struct max8997_dev *max8997)
{
- if (max8997->irq && max8997->irq_base)
- max8997_irq_thread(max8997->irq_base, max8997);
+ if (max8997->irq && max8997->irq_domain)
+ max8997_irq_thread(0, max8997);
+ return 0;
+}
+
+static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct max8997_dev *max8997 = d->host_data;
+
+ irq_set_chip_data(irq, max8997);
+ irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
return 0;
}
+static struct irq_domain_ops max8997_irq_domain_ops = {
+ .map = max8997_irq_domain_map,
+};
+
int max8997_irq_init(struct max8997_dev *max8997)
{
+ struct irq_domain *domain;
int i;
- int cur_irq;
int ret;
u8 val;
if (!max8997->irq) {
dev_warn(max8997->dev, "No interrupt specified.\n");
- max8997->irq_base = 0;
- return 0;
- }
-
- if (!max8997->irq_base) {
- dev_err(max8997->dev, "No interrupt base specified.\n");
return 0;
}
@@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997)
true : false;
}
- /* Register with genirq */
- for (i = 0; i < MAX8997_IRQ_NR; i++) {
- cur_irq = i + max8997->irq_base;
- irq_set_chip_data(cur_irq, max8997);
- irq_set_chip_and_handler(cur_irq, &max8997_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- irq_set_noprobe(cur_irq);
-#endif
+ domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
+ &max8997_irq_domain_ops, max8997);
+ if (!domain) {
+ dev_err(max8997->dev, "could not create irq domain\n");
+ return -ENODEV;
}
+ max8997->irq_domain = domain;
ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index cb83a7ab53e7..10b629c245b6 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
if (!pdata)
goto err;
- max8997->irq_base = pdata->irq_base;
max8997->ono = pdata->ono;
mutex_init(&max8997->iolock);
@@ -206,7 +205,7 @@ static const struct i2c_device_id max8997_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
-u8 max8997_dumpaddr_pmic[] = {
+static u8 max8997_dumpaddr_pmic[] = {
MAX8997_REG_INT1MSK,
MAX8997_REG_INT2MSK,
MAX8997_REG_INT3MSK,
@@ -331,7 +330,7 @@ u8 max8997_dumpaddr_pmic[] = {
MAX8997_REG_DVSOKTIMER5,
};
-u8 max8997_dumpaddr_muic[] = {
+static u8 max8997_dumpaddr_muic[] = {
MAX8997_MUIC_REG_INTMASK1,
MAX8997_MUIC_REG_INTMASK2,
MAX8997_MUIC_REG_INTMASK3,
@@ -341,7 +340,7 @@ u8 max8997_dumpaddr_muic[] = {
MAX8997_MUIC_REG_CONTROL3,
};
-u8 max8997_dumpaddr_haptic[] = {
+static u8 max8997_dumpaddr_haptic[] = {
MAX8997_HAPTIC_REG_CONF1,
MAX8997_HAPTIC_REG_CONF2,
MAX8997_HAPTIC_REG_DRVCONF,
@@ -423,7 +422,7 @@ static int max8997_resume(struct device *dev)
return max8997_irq_resume(max8997);
}
-const struct dev_pm_ops max8997_pm = {
+static const struct dev_pm_ops max8997_pm = {
.suspend = max8997_suspend,
.resume = max8997_resume,
.freeze = max8997_freeze,
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index f0ea3b8b3e4a..b801dc72f041 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -723,10 +723,6 @@ void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
free_irq(mc13xxx->irq, mc13xxx);
mfd_remove_devices(mc13xxx->dev);
-
- regmap_exit(mc13xxx->regmap);
-
- kfree(mc13xxx);
}
EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c
index d22501dad6a6..9d18dde3cd2a 100644
--- a/drivers/mfd/mc13xxx-i2c.c
+++ b/drivers/mfd/mc13xxx-i2c.c
@@ -53,17 +53,11 @@ static struct regmap_config mc13xxx_regmap_i2c_config = {
static int mc13xxx_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct of_device_id *of_id;
- struct i2c_driver *idrv = to_i2c_driver(client->dev.driver);
struct mc13xxx *mc13xxx;
struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
int ret;
- of_id = of_match_device(mc13xxx_dt_ids, &client->dev);
- if (of_id)
- idrv->id_table = (const struct i2c_device_id*) of_id->data;
-
- mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+ mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL);
if (!mc13xxx)
return -ENOMEM;
@@ -72,13 +66,13 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
mc13xxx->dev = &client->dev;
mutex_init(&mc13xxx->lock);
- mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config);
+ mc13xxx->regmap = devm_regmap_init_i2c(client,
+ &mc13xxx_regmap_i2c_config);
if (IS_ERR(mc13xxx->regmap)) {
ret = PTR_ERR(mc13xxx->regmap);
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
ret);
dev_set_drvdata(&client->dev, NULL);
- kfree(mc13xxx);
return ret;
}
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
index 03df422feb76..0bdb43a0aff0 100644
--- a/drivers/mfd/mc13xxx-spi.c
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -119,17 +119,11 @@ static struct regmap_bus regmap_mc13xxx_bus = {
static int mc13xxx_spi_probe(struct spi_device *spi)
{
- const struct of_device_id *of_id;
- struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
struct mc13xxx *mc13xxx;
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
int ret;
- of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
- if (of_id)
- sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
-
- mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+ mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);
if (!mc13xxx)
return -ENOMEM;
@@ -139,15 +133,14 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
mc13xxx->dev = &spi->dev;
mutex_init(&mc13xxx->lock);
- mc13xxx->regmap = regmap_init(&spi->dev, &regmap_mc13xxx_bus, &spi->dev,
- &mc13xxx_regmap_spi_config);
-
+ mc13xxx->regmap = devm_regmap_init(&spi->dev, &regmap_mc13xxx_bus,
+ &spi->dev,
+ &mc13xxx_regmap_spi_config);
if (IS_ERR(mc13xxx->regmap)) {
ret = PTR_ERR(mc13xxx->regmap);
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
ret);
dev_set_drvdata(&spi->dev, NULL);
- kfree(mc13xxx);
return ret;
}
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index ffc3d48676ae..0c3a01cde2f7 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -18,6 +18,8 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
int mfd_cell_enable(struct platform_device *pdev)
{
@@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id,
{
struct resource *res;
struct platform_device *pdev;
+ struct device_node *np = NULL;
+ struct irq_domain *domain = NULL;
int ret = -ENOMEM;
int r;
@@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id,
pdev->dev.parent = parent;
+ if (parent->of_node && cell->of_compatible) {
+ for_each_child_of_node(parent->of_node, np) {
+ if (of_device_is_compatible(np, cell->of_compatible)) {
+ pdev->dev.of_node = np;
+ domain = irq_find_host(parent->of_node);
+ break;
+ }
+ }
+ }
+
if (cell->pdata_size) {
ret = platform_device_add_data(pdev,
cell->platform_data, cell->pdata_size);
@@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id,
res[r].end = mem_base->start +
cell->resources[r].end;
} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
- res[r].start = irq_base +
- cell->resources[r].start;
- res[r].end = irq_base +
- cell->resources[r].end;
+ if (domain) {
+ /* Unable to create mappings for IRQ ranges. */
+ WARN_ON(cell->resources[r].start !=
+ cell->resources[r].end);
+ res[r].start = res[r].end = irq_create_mapping(
+ domain, cell->resources[r].start);
+ } else {
+ res[r].start = irq_base +
+ cell->resources[r].start;
+ res[r].end = irq_base +
+ cell->resources[r].end;
+ }
} else {
res[r].parent = cell->resources[r].parent;
res[r].start = cell->resources[r].start;
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index 29c122bf28ea..45ce1fb5a549 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -253,8 +253,13 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
}
pdev->dev.parent = pcf->dev;
- platform_device_add_data(pdev, &pdata->reg_init_data[i],
- sizeof(pdata->reg_init_data[i]));
+ if (platform_device_add_data(pdev, &pdata->reg_init_data[i],
+ sizeof(pdata->reg_init_data[i])) < 0) {
+ platform_device_put(pdev);
+ dev_err(pcf->dev, "Out of memory for regulator parameters %d\n",
+ i);
+ continue;
+ }
pcf->regulator_pdev[i] = pdev;
platform_device_add(pdev);
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c
deleted file mode 100644
index dd170307e60e..000000000000
--- a/drivers/mfd/s5m-core.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * s5m87xx.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- * http://www.samsung.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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/pm_runtime.h>
-#include <linux/mutex.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-#include <linux/mfd/s5m87xx/s5m-pmic.h>
-#include <linux/mfd/s5m87xx/s5m-rtc.h>
-#include <linux/regmap.h>
-
-static struct mfd_cell s5m8751_devs[] = {
- {
- .name = "s5m8751-pmic",
- }, {
- .name = "s5m-charger",
- }, {
- .name = "s5m8751-codec",
- },
-};
-
-static struct mfd_cell s5m8763_devs[] = {
- {
- .name = "s5m8763-pmic",
- }, {
- .name = "s5m-rtc",
- }, {
- .name = "s5m-charger",
- },
-};
-
-static struct mfd_cell s5m8767_devs[] = {
- {
- .name = "s5m8767-pmic",
- }, {
- .name = "s5m-rtc",
- },
-};
-
-int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest)
-{
- return regmap_read(s5m87xx->regmap, reg, dest);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_read);
-
-int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
-{
- return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);
-}
-EXPORT_SYMBOL_GPL(s5m_bulk_read);
-
-int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value)
-{
- return regmap_write(s5m87xx->regmap, reg, value);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_write);
-
-int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
-{
- return regmap_raw_write(s5m87xx->regmap, reg, buf, count);
-}
-EXPORT_SYMBOL_GPL(s5m_bulk_write);
-
-int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask)
-{
- return regmap_update_bits(s5m87xx->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(s5m_reg_update);
-
-static struct regmap_config s5m_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
-static int s5m87xx_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct s5m_platform_data *pdata = i2c->dev.platform_data;
- struct s5m87xx_dev *s5m87xx;
- int ret;
-
- s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
- GFP_KERNEL);
- if (s5m87xx == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, s5m87xx);
- s5m87xx->dev = &i2c->dev;
- s5m87xx->i2c = i2c;
- s5m87xx->irq = i2c->irq;
- s5m87xx->type = id->driver_data;
-
- if (pdata) {
- s5m87xx->device_type = pdata->device_type;
- s5m87xx->ono = pdata->ono;
- s5m87xx->irq_base = pdata->irq_base;
- s5m87xx->wakeup = pdata->wakeup;
- }
-
- s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config);
- if (IS_ERR(s5m87xx->regmap)) {
- ret = PTR_ERR(s5m87xx->regmap);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
- return ret;
- }
-
- s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
- i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
-
- if (pdata && pdata->cfg_pmic_irq)
- pdata->cfg_pmic_irq();
-
- s5m_irq_init(s5m87xx);
-
- pm_runtime_set_active(s5m87xx->dev);
-
- switch (s5m87xx->device_type) {
- case S5M8751X:
- ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
- ARRAY_SIZE(s5m8751_devs), NULL, 0);
- break;
- case S5M8763X:
- ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
- ARRAY_SIZE(s5m8763_devs), NULL, 0);
- break;
- case S5M8767X:
- ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
- ARRAY_SIZE(s5m8767_devs), NULL, 0);
- break;
- default:
- /* If this happens the probe function is problem */
- BUG();
- }
-
- if (ret < 0)
- goto err;
-
- return ret;
-
-err:
- mfd_remove_devices(s5m87xx->dev);
- s5m_irq_exit(s5m87xx);
- i2c_unregister_device(s5m87xx->rtc);
- return ret;
-}
-
-static int s5m87xx_i2c_remove(struct i2c_client *i2c)
-{
- struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
-
- mfd_remove_devices(s5m87xx->dev);
- s5m_irq_exit(s5m87xx);
- i2c_unregister_device(s5m87xx->rtc);
- return 0;
-}
-
-static const struct i2c_device_id s5m87xx_i2c_id[] = {
- { "s5m87xx", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
-
-static struct i2c_driver s5m87xx_i2c_driver = {
- .driver = {
- .name = "s5m87xx",
- .owner = THIS_MODULE,
- },
- .probe = s5m87xx_i2c_probe,
- .remove = s5m87xx_i2c_remove,
- .id_table = s5m87xx_i2c_id,
-};
-
-static int __init s5m87xx_i2c_init(void)
-{
- return i2c_add_driver(&s5m87xx_i2c_driver);
-}
-
-subsys_initcall(s5m87xx_i2c_init);
-
-static void __exit s5m87xx_i2c_exit(void)
-{
- i2c_del_driver(&s5m87xx_i2c_driver);
-}
-module_exit(s5m87xx_i2c_exit);
-
-MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
-MODULE_DESCRIPTION("Core support for the S5M MFD");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c
deleted file mode 100644
index 0236676085cf..000000000000
--- a/drivers/mfd/s5m-irq.c
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * s5m-irq.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- * http://www.samsung.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.
- *
- */
-
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-
-struct s5m_irq_data {
- int reg;
- int mask;
-};
-
-static struct s5m_irq_data s5m8767_irqs[] = {
- [S5M8767_IRQ_PWRR] = {
- .reg = 1,
- .mask = S5M8767_IRQ_PWRR_MASK,
- },
- [S5M8767_IRQ_PWRF] = {
- .reg = 1,
- .mask = S5M8767_IRQ_PWRF_MASK,
- },
- [S5M8767_IRQ_PWR1S] = {
- .reg = 1,
- .mask = S5M8767_IRQ_PWR1S_MASK,
- },
- [S5M8767_IRQ_JIGR] = {
- .reg = 1,
- .mask = S5M8767_IRQ_JIGR_MASK,
- },
- [S5M8767_IRQ_JIGF] = {
- .reg = 1,
- .mask = S5M8767_IRQ_JIGF_MASK,
- },
- [S5M8767_IRQ_LOWBAT2] = {
- .reg = 1,
- .mask = S5M8767_IRQ_LOWBAT2_MASK,
- },
- [S5M8767_IRQ_LOWBAT1] = {
- .reg = 1,
- .mask = S5M8767_IRQ_LOWBAT1_MASK,
- },
- [S5M8767_IRQ_MRB] = {
- .reg = 2,
- .mask = S5M8767_IRQ_MRB_MASK,
- },
- [S5M8767_IRQ_DVSOK2] = {
- .reg = 2,
- .mask = S5M8767_IRQ_DVSOK2_MASK,
- },
- [S5M8767_IRQ_DVSOK3] = {
- .reg = 2,
- .mask = S5M8767_IRQ_DVSOK3_MASK,
- },
- [S5M8767_IRQ_DVSOK4] = {
- .reg = 2,
- .mask = S5M8767_IRQ_DVSOK4_MASK,
- },
- [S5M8767_IRQ_RTC60S] = {
- .reg = 3,
- .mask = S5M8767_IRQ_RTC60S_MASK,
- },
- [S5M8767_IRQ_RTCA1] = {
- .reg = 3,
- .mask = S5M8767_IRQ_RTCA1_MASK,
- },
- [S5M8767_IRQ_RTCA2] = {
- .reg = 3,
- .mask = S5M8767_IRQ_RTCA2_MASK,
- },
- [S5M8767_IRQ_SMPL] = {
- .reg = 3,
- .mask = S5M8767_IRQ_SMPL_MASK,
- },
- [S5M8767_IRQ_RTC1S] = {
- .reg = 3,
- .mask = S5M8767_IRQ_RTC1S_MASK,
- },
- [S5M8767_IRQ_WTSR] = {
- .reg = 3,
- .mask = S5M8767_IRQ_WTSR_MASK,
- },
-};
-
-static struct s5m_irq_data s5m8763_irqs[] = {
- [S5M8763_IRQ_DCINF] = {
- .reg = 1,
- .mask = S5M8763_IRQ_DCINF_MASK,
- },
- [S5M8763_IRQ_DCINR] = {
- .reg = 1,
- .mask = S5M8763_IRQ_DCINR_MASK,
- },
- [S5M8763_IRQ_JIGF] = {
- .reg = 1,
- .mask = S5M8763_IRQ_JIGF_MASK,
- },
- [S5M8763_IRQ_JIGR] = {
- .reg = 1,
- .mask = S5M8763_IRQ_JIGR_MASK,
- },
- [S5M8763_IRQ_PWRONF] = {
- .reg = 1,
- .mask = S5M8763_IRQ_PWRONF_MASK,
- },
- [S5M8763_IRQ_PWRONR] = {
- .reg = 1,
- .mask = S5M8763_IRQ_PWRONR_MASK,
- },
- [S5M8763_IRQ_WTSREVNT] = {
- .reg = 2,
- .mask = S5M8763_IRQ_WTSREVNT_MASK,
- },
- [S5M8763_IRQ_SMPLEVNT] = {
- .reg = 2,
- .mask = S5M8763_IRQ_SMPLEVNT_MASK,
- },
- [S5M8763_IRQ_ALARM1] = {
- .reg = 2,
- .mask = S5M8763_IRQ_ALARM1_MASK,
- },
- [S5M8763_IRQ_ALARM0] = {
- .reg = 2,
- .mask = S5M8763_IRQ_ALARM0_MASK,
- },
- [S5M8763_IRQ_ONKEY1S] = {
- .reg = 3,
- .mask = S5M8763_IRQ_ONKEY1S_MASK,
- },
- [S5M8763_IRQ_TOPOFFR] = {
- .reg = 3,
- .mask = S5M8763_IRQ_TOPOFFR_MASK,
- },
- [S5M8763_IRQ_DCINOVPR] = {
- .reg = 3,
- .mask = S5M8763_IRQ_DCINOVPR_MASK,
- },
- [S5M8763_IRQ_CHGRSTF] = {
- .reg = 3,
- .mask = S5M8763_IRQ_CHGRSTF_MASK,
- },
- [S5M8763_IRQ_DONER] = {
- .reg = 3,
- .mask = S5M8763_IRQ_DONER_MASK,
- },
- [S5M8763_IRQ_CHGFAULT] = {
- .reg = 3,
- .mask = S5M8763_IRQ_CHGFAULT_MASK,
- },
- [S5M8763_IRQ_LOBAT1] = {
- .reg = 4,
- .mask = S5M8763_IRQ_LOBAT1_MASK,
- },
- [S5M8763_IRQ_LOBAT2] = {
- .reg = 4,
- .mask = S5M8763_IRQ_LOBAT2_MASK,
- },
-};
-
-static inline struct s5m_irq_data *
-irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
-{
- return &s5m8767_irqs[irq - s5m87xx->irq_base];
-}
-
-static void s5m8767_irq_lock(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&s5m87xx->irqlock);
-}
-
-static void s5m8767_irq_sync_unlock(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
- if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
- s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
- s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
- s5m87xx->irq_masks_cur[i]);
- }
- }
-
- mutex_unlock(&s5m87xx->irqlock);
-}
-
-static void s5m8767_irq_unmask(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
- data->irq);
-
- s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
-}
-
-static void s5m8767_irq_mask(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
- data->irq);
-
- s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
-}
-
-static struct irq_chip s5m8767_irq_chip = {
- .name = "s5m8767",
- .irq_bus_lock = s5m8767_irq_lock,
- .irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
- .irq_mask = s5m8767_irq_mask,
- .irq_unmask = s5m8767_irq_unmask,
-};
-
-static inline struct s5m_irq_data *
-irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
-{
- return &s5m8763_irqs[irq - s5m87xx->irq_base];
-}
-
-static void s5m8763_irq_lock(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
-
- mutex_lock(&s5m87xx->irqlock);
-}
-
-static void s5m8763_irq_sync_unlock(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
- if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
- s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
- s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
- s5m87xx->irq_masks_cur[i]);
- }
- }
-
- mutex_unlock(&s5m87xx->irqlock);
-}
-
-static void s5m8763_irq_unmask(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
- data->irq);
-
- s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
-}
-
-static void s5m8763_irq_mask(struct irq_data *data)
-{
- struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
- struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
- data->irq);
-
- s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
-}
-
-static struct irq_chip s5m8763_irq_chip = {
- .name = "s5m8763",
- .irq_bus_lock = s5m8763_irq_lock,
- .irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
- .irq_mask = s5m8763_irq_mask,
- .irq_unmask = s5m8763_irq_unmask,
-};
-
-
-static irqreturn_t s5m8767_irq_thread(int irq, void *data)
-{
- struct s5m87xx_dev *s5m87xx = data;
- u8 irq_reg[NUM_IRQ_REGS-1];
- int ret;
- int i;
-
-
- ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1,
- NUM_IRQ_REGS - 1, irq_reg);
- if (ret < 0) {
- dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
- ret);
- return IRQ_NONE;
- }
-
- for (i = 0; i < NUM_IRQ_REGS - 1; i++)
- irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
-
- for (i = 0; i < S5M8767_IRQ_NR; i++) {
- if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
- handle_nested_irq(s5m87xx->irq_base + i);
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t s5m8763_irq_thread(int irq, void *data)
-{
- struct s5m87xx_dev *s5m87xx = data;
- u8 irq_reg[NUM_IRQ_REGS];
- int ret;
- int i;
-
- ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1,
- NUM_IRQ_REGS, irq_reg);
- if (ret < 0) {
- dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
- ret);
- return IRQ_NONE;
- }
-
- for (i = 0; i < NUM_IRQ_REGS; i++)
- irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
-
- for (i = 0; i < S5M8763_IRQ_NR; i++) {
- if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
- handle_nested_irq(s5m87xx->irq_base + i);
- }
-
- return IRQ_HANDLED;
-}
-
-int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
-{
- if (s5m87xx->irq && s5m87xx->irq_base){
- switch (s5m87xx->device_type) {
- case S5M8763X:
- s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
- break;
- case S5M8767X:
- s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
- break;
- default:
- dev_err(s5m87xx->dev,
- "Unknown device type %d\n",
- s5m87xx->device_type);
- return -EINVAL;
-
- }
- }
- return 0;
-}
-
-int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
-{
- int i;
- int cur_irq;
- int ret = 0;
- int type = s5m87xx->device_type;
-
- if (!s5m87xx->irq) {
- dev_warn(s5m87xx->dev,
- "No interrupt specified, no interrupts\n");
- s5m87xx->irq_base = 0;
- return 0;
- }
-
- if (!s5m87xx->irq_base) {
- dev_err(s5m87xx->dev,
- "No interrupt base specified, no interrupts\n");
- return 0;
- }
-
- mutex_init(&s5m87xx->irqlock);
-
- switch (type) {
- case S5M8763X:
- for (i = 0; i < NUM_IRQ_REGS; i++) {
- s5m87xx->irq_masks_cur[i] = 0xff;
- s5m87xx->irq_masks_cache[i] = 0xff;
- s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
- 0xff);
- }
-
- s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff);
- s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff);
-
- for (i = 0; i < S5M8763_IRQ_NR; i++) {
- cur_irq = i + s5m87xx->irq_base;
- irq_set_chip_data(cur_irq, s5m87xx);
- irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- irq_set_noprobe(cur_irq);
-#endif
- }
-
- ret = request_threaded_irq(s5m87xx->irq, NULL,
- s5m8763_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "s5m87xx-irq", s5m87xx);
- if (ret) {
- dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
- s5m87xx->irq, ret);
- return ret;
- }
- break;
- case S5M8767X:
- for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
- s5m87xx->irq_masks_cur[i] = 0xff;
- s5m87xx->irq_masks_cache[i] = 0xff;
- s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
- 0xff);
- }
- for (i = 0; i < S5M8767_IRQ_NR; i++) {
- cur_irq = i + s5m87xx->irq_base;
- irq_set_chip_data(cur_irq, s5m87xx);
- if (ret) {
- dev_err(s5m87xx->dev,
- "Failed to irq_set_chip_data %d: %d\n",
- s5m87xx->irq, ret);
- return ret;
- }
-
- irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(cur_irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(cur_irq, IRQF_VALID);
-#else
- irq_set_noprobe(cur_irq);
-#endif
- }
-
- ret = request_threaded_irq(s5m87xx->irq, NULL,
- s5m8767_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "s5m87xx-irq", s5m87xx);
- if (ret) {
- dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
- s5m87xx->irq, ret);
- return ret;
- }
- break;
- default:
- dev_err(s5m87xx->dev,
- "Unknown device type %d\n", s5m87xx->device_type);
- return -EINVAL;
- }
-
- if (!s5m87xx->ono)
- return 0;
-
- switch (type) {
- case S5M8763X:
- ret = request_threaded_irq(s5m87xx->ono, NULL,
- s5m8763_irq_thread,
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "s5m87xx-ono",
- s5m87xx);
- break;
- case S5M8767X:
- ret = request_threaded_irq(s5m87xx->ono, NULL,
- s5m8767_irq_thread,
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- if (ret) {
- dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
- s5m87xx->ono, ret);
- return ret;
- }
-
- return 0;
-}
-
-void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
-{
- if (s5m87xx->ono)
- free_irq(s5m87xx->ono, s5m87xx);
-
- if (s5m87xx->irq)
- free_irq(s5m87xx->irq, s5m87xx);
-}
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
new file mode 100644
index 000000000000..2988efde11eb
--- /dev/null
+++ b/drivers/mfd/sec-core.c
@@ -0,0 +1,216 @@
+/*
+ * sec-core.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ * http://www.samsung.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/rtc.h>
+#include <linux/regmap.h>
+
+static struct mfd_cell s5m8751_devs[] = {
+ {
+ .name = "s5m8751-pmic",
+ }, {
+ .name = "s5m-charger",
+ }, {
+ .name = "s5m8751-codec",
+ },
+};
+
+static struct mfd_cell s5m8763_devs[] = {
+ {
+ .name = "s5m8763-pmic",
+ }, {
+ .name = "s5m-rtc",
+ }, {
+ .name = "s5m-charger",
+ },
+};
+
+static struct mfd_cell s5m8767_devs[] = {
+ {
+ .name = "s5m8767-pmic",
+ }, {
+ .name = "s5m-rtc",
+ },
+};
+
+static struct mfd_cell s2mps11_devs[] = {
+ {
+ .name = "s2mps11-pmic",
+ },
+};
+
+int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest)
+{
+ return regmap_read(sec_pmic->regmap, reg, dest);
+}
+EXPORT_SYMBOL_GPL(sec_reg_read);
+
+int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+{
+ return regmap_bulk_read(sec_pmic->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(sec_bulk_read);
+
+int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value)
+{
+ return regmap_write(sec_pmic->regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(sec_reg_write);
+
+int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
+{
+ return regmap_raw_write(sec_pmic->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(sec_bulk_write);
+
+int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask)
+{
+ return regmap_update_bits(sec_pmic->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(sec_reg_update);
+
+static struct regmap_config sec_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int sec_pmic_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct sec_platform_data *pdata = i2c->dev.platform_data;
+ struct sec_pmic_dev *sec_pmic;
+ int ret;
+
+ sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
+ GFP_KERNEL);
+ if (sec_pmic == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, sec_pmic);
+ sec_pmic->dev = &i2c->dev;
+ sec_pmic->i2c = i2c;
+ sec_pmic->irq = i2c->irq;
+ sec_pmic->type = id->driver_data;
+
+ if (pdata) {
+ sec_pmic->device_type = pdata->device_type;
+ sec_pmic->ono = pdata->ono;
+ sec_pmic->irq_base = pdata->irq_base;
+ sec_pmic->wakeup = pdata->wakeup;
+ }
+
+ sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config);
+ if (IS_ERR(sec_pmic->regmap)) {
+ ret = PTR_ERR(sec_pmic->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
+
+ if (pdata && pdata->cfg_pmic_irq)
+ pdata->cfg_pmic_irq();
+
+ sec_irq_init(sec_pmic);
+
+ pm_runtime_set_active(sec_pmic->dev);
+
+ switch (sec_pmic->device_type) {
+ case S5M8751X:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
+ ARRAY_SIZE(s5m8751_devs), NULL, 0);
+ break;
+ case S5M8763X:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
+ ARRAY_SIZE(s5m8763_devs), NULL, 0);
+ break;
+ case S5M8767X:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
+ ARRAY_SIZE(s5m8767_devs), NULL, 0);
+ break;
+ case S2MPS11X:
+ ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
+ ARRAY_SIZE(s2mps11_devs), NULL, 0);
+ break;
+ default:
+ /* If this happens the probe function is problem */
+ BUG();
+ }
+
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(sec_pmic->dev);
+ sec_irq_exit(sec_pmic);
+ i2c_unregister_device(sec_pmic->rtc);
+ return ret;
+}
+
+static int sec_pmic_remove(struct i2c_client *i2c)
+{
+ struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(sec_pmic->dev);
+ sec_irq_exit(sec_pmic);
+ i2c_unregister_device(sec_pmic->rtc);
+ return 0;
+}
+
+static const struct i2c_device_id sec_pmic_id[] = {
+ { "sec_pmic", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sec_pmic_id);
+
+static struct i2c_driver sec_pmic_driver = {
+ .driver = {
+ .name = "sec_pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = sec_pmic_probe,
+ .remove = sec_pmic_remove,
+ .id_table = sec_pmic_id,
+};
+
+static int __init sec_pmic_init(void)
+{
+ return i2c_add_driver(&sec_pmic_driver);
+}
+
+subsys_initcall(sec_pmic_init);
+
+static void __exit sec_pmic_exit(void)
+{
+ i2c_del_driver(&sec_pmic_driver);
+}
+module_exit(sec_pmic_exit);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Core support for the S5M MFD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
new file mode 100644
index 000000000000..c901fa50fea1
--- /dev/null
+++ b/drivers/mfd/sec-irq.c
@@ -0,0 +1,317 @@
+/*
+ * sec-irq.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * http://www.samsung.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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s5m8763.h>
+#include <linux/mfd/samsung/s5m8767.h>
+
+static struct regmap_irq s2mps11_irqs[] = {
+ [S2MPS11_IRQ_PWRONF] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_PWRONF_MASK,
+ },
+ [S2MPS11_IRQ_PWRONR] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_PWRONR_MASK,
+ },
+ [S2MPS11_IRQ_JIGONBF] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_JIGONBF_MASK,
+ },
+ [S2MPS11_IRQ_JIGONBR] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_JIGONBR_MASK,
+ },
+ [S2MPS11_IRQ_ACOKBF] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_ACOKBF_MASK,
+ },
+ [S2MPS11_IRQ_ACOKBR] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_ACOKBR_MASK,
+ },
+ [S2MPS11_IRQ_PWRON1S] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_PWRON1S_MASK,
+ },
+ [S2MPS11_IRQ_MRB] = {
+ .reg_offset = 1,
+ .mask = S2MPS11_IRQ_MRB_MASK,
+ },
+ [S2MPS11_IRQ_RTC60S] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_RTC60S_MASK,
+ },
+ [S2MPS11_IRQ_RTCA1] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_RTCA1_MASK,
+ },
+ [S2MPS11_IRQ_RTCA2] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_RTCA2_MASK,
+ },
+ [S2MPS11_IRQ_SMPL] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_SMPL_MASK,
+ },
+ [S2MPS11_IRQ_RTC1S] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_RTC1S_MASK,
+ },
+ [S2MPS11_IRQ_WTSR] = {
+ .reg_offset = 2,
+ .mask = S2MPS11_IRQ_WTSR_MASK,
+ },
+ [S2MPS11_IRQ_INT120C] = {
+ .reg_offset = 3,
+ .mask = S2MPS11_IRQ_INT120C_MASK,
+ },
+ [S2MPS11_IRQ_INT140C] = {
+ .reg_offset = 3,
+ .mask = S2MPS11_IRQ_INT140C_MASK,
+ },
+};
+
+
+static struct regmap_irq s5m8767_irqs[] = {
+ [S5M8767_IRQ_PWRR] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_PWRR_MASK,
+ },
+ [S5M8767_IRQ_PWRF] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_PWRF_MASK,
+ },
+ [S5M8767_IRQ_PWR1S] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_PWR1S_MASK,
+ },
+ [S5M8767_IRQ_JIGR] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_JIGR_MASK,
+ },
+ [S5M8767_IRQ_JIGF] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_JIGF_MASK,
+ },
+ [S5M8767_IRQ_LOWBAT2] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_LOWBAT2_MASK,
+ },
+ [S5M8767_IRQ_LOWBAT1] = {
+ .reg_offset = 1,
+ .mask = S5M8767_IRQ_LOWBAT1_MASK,
+ },
+ [S5M8767_IRQ_MRB] = {
+ .reg_offset = 2,
+ .mask = S5M8767_IRQ_MRB_MASK,
+ },
+ [S5M8767_IRQ_DVSOK2] = {
+ .reg_offset = 2,
+ .mask = S5M8767_IRQ_DVSOK2_MASK,
+ },
+ [S5M8767_IRQ_DVSOK3] = {
+ .reg_offset = 2,
+ .mask = S5M8767_IRQ_DVSOK3_MASK,
+ },
+ [S5M8767_IRQ_DVSOK4] = {
+ .reg_offset = 2,
+ .mask = S5M8767_IRQ_DVSOK4_MASK,
+ },
+ [S5M8767_IRQ_RTC60S] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_RTC60S_MASK,
+ },
+ [S5M8767_IRQ_RTCA1] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_RTCA1_MASK,
+ },
+ [S5M8767_IRQ_RTCA2] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_RTCA2_MASK,
+ },
+ [S5M8767_IRQ_SMPL] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_SMPL_MASK,
+ },
+ [S5M8767_IRQ_RTC1S] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_RTC1S_MASK,
+ },
+ [S5M8767_IRQ_WTSR] = {
+ .reg_offset = 3,
+ .mask = S5M8767_IRQ_WTSR_MASK,
+ },
+};
+
+static struct regmap_irq s5m8763_irqs[] = {
+ [S5M8763_IRQ_DCINF] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_DCINF_MASK,
+ },
+ [S5M8763_IRQ_DCINR] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_DCINR_MASK,
+ },
+ [S5M8763_IRQ_JIGF] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_JIGF_MASK,
+ },
+ [S5M8763_IRQ_JIGR] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_JIGR_MASK,
+ },
+ [S5M8763_IRQ_PWRONF] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_PWRONF_MASK,
+ },
+ [S5M8763_IRQ_PWRONR] = {
+ .reg_offset = 1,
+ .mask = S5M8763_IRQ_PWRONR_MASK,
+ },
+ [S5M8763_IRQ_WTSREVNT] = {
+ .reg_offset = 2,
+ .mask = S5M8763_IRQ_WTSREVNT_MASK,
+ },
+ [S5M8763_IRQ_SMPLEVNT] = {
+ .reg_offset = 2,
+ .mask = S5M8763_IRQ_SMPLEVNT_MASK,
+ },
+ [S5M8763_IRQ_ALARM1] = {
+ .reg_offset = 2,
+ .mask = S5M8763_IRQ_ALARM1_MASK,
+ },
+ [S5M8763_IRQ_ALARM0] = {
+ .reg_offset = 2,
+ .mask = S5M8763_IRQ_ALARM0_MASK,
+ },
+ [S5M8763_IRQ_ONKEY1S] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_ONKEY1S_MASK,
+ },
+ [S5M8763_IRQ_TOPOFFR] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_TOPOFFR_MASK,
+ },
+ [S5M8763_IRQ_DCINOVPR] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_DCINOVPR_MASK,
+ },
+ [S5M8763_IRQ_CHGRSTF] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_CHGRSTF_MASK,
+ },
+ [S5M8763_IRQ_DONER] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_DONER_MASK,
+ },
+ [S5M8763_IRQ_CHGFAULT] = {
+ .reg_offset = 3,
+ .mask = S5M8763_IRQ_CHGFAULT_MASK,
+ },
+ [S5M8763_IRQ_LOBAT1] = {
+ .reg_offset = 4,
+ .mask = S5M8763_IRQ_LOBAT1_MASK,
+ },
+ [S5M8763_IRQ_LOBAT2] = {
+ .reg_offset = 4,
+ .mask = S5M8763_IRQ_LOBAT2_MASK,
+ },
+};
+
+static struct regmap_irq_chip s2mps11_irq_chip = {
+ .name = "s2mps11",
+ .irqs = s2mps11_irqs,
+ .num_irqs = ARRAY_SIZE(s2mps11_irqs),
+ .num_regs = 3,
+ .status_base = S2MPS11_REG_INT1,
+ .mask_base = S2MPS11_REG_INT1M,
+ .ack_base = S2MPS11_REG_INT1,
+};
+
+static struct regmap_irq_chip s5m8767_irq_chip = {
+ .name = "s5m8767",
+ .irqs = s5m8767_irqs,
+ .num_irqs = ARRAY_SIZE(s5m8767_irqs),
+ .num_regs = 3,
+ .status_base = S5M8767_REG_INT1,
+ .mask_base = S5M8767_REG_INT1M,
+ .ack_base = S5M8767_REG_INT1,
+};
+
+static struct regmap_irq_chip s5m8763_irq_chip = {
+ .name = "s5m8763",
+ .irqs = s5m8763_irqs,
+ .num_irqs = ARRAY_SIZE(s5m8763_irqs),
+ .num_regs = 4,
+ .status_base = S5M8763_REG_IRQ1,
+ .mask_base = S5M8763_REG_IRQM1,
+ .ack_base = S5M8763_REG_IRQ1,
+};
+
+int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+{
+ int ret = 0;
+ int type = sec_pmic->device_type;
+
+ if (!sec_pmic->irq) {
+ dev_warn(sec_pmic->dev,
+ "No interrupt specified, no interrupts\n");
+ sec_pmic->irq_base = 0;
+ return 0;
+ }
+
+ switch (type) {
+ case S5M8763X:
+ ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ sec_pmic->irq_base, &s5m8763_irq_chip,
+ &sec_pmic->irq_data);
+ break;
+ case S5M8767X:
+ ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ sec_pmic->irq_base, &s5m8767_irq_chip,
+ &sec_pmic->irq_data);
+ break;
+ case S2MPS11X:
+ ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ sec_pmic->irq_base, &s2mps11_irq_chip,
+ &sec_pmic->irq_data);
+ break;
+ default:
+ dev_err(sec_pmic->dev, "Unknown device type %d\n",
+ sec_pmic->device_type);
+ return -EINVAL;
+ }
+
+ if (ret != 0) {
+ dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void sec_irq_exit(struct sec_pmic_dev *sec_pmic)
+{
+ regmap_del_irq_chip(sec_pmic->irq, sec_pmic->irq_data);
+}
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index de979742c6fc..048bf0532a09 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -357,7 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int tc3589x_suspend(struct device *dev)
{
struct tc3589x *tc3589x = dev_get_drvdata(dev);
@@ -385,11 +385,10 @@ static int tc3589x_resume(struct device *dev)
return ret;
}
-
-static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
- tc3589x_resume);
#endif
+static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
+
static const struct i2c_device_id tc3589x_id[] = {
{ "tc3589x", 24 },
{ }
@@ -399,9 +398,7 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id);
static struct i2c_driver tc3589x_driver = {
.driver.name = "tc3589x",
.driver.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.driver.pm = &tc3589x_dev_pm_ops,
-#endif
.probe = tc3589x_probe,
.remove = __devexit_p(tc3589x_remove),
.id_table = tc3589x_id,
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 0ba26fb12cf5..a447f4ec11fb 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -83,7 +83,7 @@ timberdale_xiic_platform_data = {
static __devinitdata struct ocores_i2c_platform_data
timberdale_ocores_platform_data = {
- .regstep = 4,
+ .reg_shift = 2,
.clock_khz = 62500,
.devices = timberdale_i2c_board_info,
.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index 93d5fdf020c7..da2691f22e11 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -563,8 +563,7 @@ static int tps65010_probe(struct i2c_client *client,
*/
if (client->irq > 0) {
status = request_irq(client->irq, tps65010_irq,
- IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
- DRIVER_NAME, tps);
+ IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
if (status < 0) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
client->irq, status);
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index 396b9d1b6bd6..80e24f4b47bf 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -71,10 +71,10 @@ static const struct tps65090_irq_data tps65090_irqs[] = {
static struct mfd_cell tps65090s[] = {
{
- .name = "tps65910-pmic",
+ .name = "tps65090-pmic",
},
{
- .name = "tps65910-regulator",
+ .name = "tps65090-regulator",
},
};
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index c84b5506d5fb..353c34812120 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -21,17 +21,14 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6586x.h>
-/* GPIO control registers */
-#define TPS6586X_GPIOSET1 0x5d
-#define TPS6586X_GPIOSET2 0x5e
-
/* interrupt control registers */
#define TPS6586X_INT_ACK1 0xb5
#define TPS6586X_INT_ACK2 0xb6
@@ -48,6 +45,9 @@
/* device id */
#define TPS6586X_VERSIONCRC 0xcd
+/* Maximum register */
+#define TPS6586X_MAX_REGISTER (TPS6586X_VERSIONCRC + 1)
+
struct tps6586x_irq_data {
u8 mask_reg;
u8 mask_mask;
@@ -89,226 +89,96 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = {
[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
};
+static struct mfd_cell tps6586x_cell[] = {
+ {
+ .name = "tps6586x-gpio",
+ },
+ {
+ .name = "tps6586x-rtc",
+ },
+ {
+ .name = "tps6586x-onkey",
+ },
+};
+
struct tps6586x {
- struct mutex lock;
struct device *dev;
struct i2c_client *client;
+ struct regmap *regmap;
- struct gpio_chip gpio;
struct irq_chip irq_chip;
struct mutex irq_lock;
int irq_base;
u32 irq_en;
- u8 mask_cache[5];
u8 mask_reg[5];
};
-static inline int __tps6586x_read(struct i2c_client *client,
- int reg, uint8_t *val)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, reg);
- if (ret < 0) {
- dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
- return ret;
- }
-
- *val = (uint8_t)ret;
-
- return 0;
-}
-
-static inline int __tps6586x_reads(struct i2c_client *client, int reg,
- int len, uint8_t *val)
-{
- int ret;
-
- ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
- if (ret < 0) {
- dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
- return ret;
- }
-
- return 0;
-}
-
-static inline int __tps6586x_write(struct i2c_client *client,
- int reg, uint8_t val)
+static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, reg, val);
- if (ret < 0) {
- dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
- val, reg);
- return ret;
- }
-
- return 0;
-}
-
-static inline int __tps6586x_writes(struct i2c_client *client, int reg,
- int len, uint8_t *val)
-{
- int ret, i;
-
- for (i = 0; i < len; i++) {
- ret = __tps6586x_write(client, reg + i, *(val + i));
- if (ret < 0)
- return ret;
- }
-
- return 0;
+ return i2c_get_clientdata(to_i2c_client(dev));
}
int tps6586x_write(struct device *dev, int reg, uint8_t val)
{
- return __tps6586x_write(to_i2c_client(dev), reg, val);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+ return regmap_write(tps6586x->regmap, reg, val);
}
EXPORT_SYMBOL_GPL(tps6586x_write);
int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
{
- return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+ return regmap_bulk_write(tps6586x->regmap, reg, val, len);
}
EXPORT_SYMBOL_GPL(tps6586x_writes);
int tps6586x_read(struct device *dev, int reg, uint8_t *val)
{
- return __tps6586x_read(to_i2c_client(dev), reg, val);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+ unsigned int rval;
+ int ret;
+
+ ret = regmap_read(tps6586x->regmap, reg, &rval);
+ if (!ret)
+ *val = rval;
+ return ret;
}
EXPORT_SYMBOL_GPL(tps6586x_read);
int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
{
- return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
+
+ return regmap_bulk_read(tps6586x->regmap, reg, val, len);
}
EXPORT_SYMBOL_GPL(tps6586x_reads);
int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
{
- struct tps6586x *tps6586x = dev_get_drvdata(dev);
- uint8_t reg_val;
- int ret = 0;
-
- mutex_lock(&tps6586x->lock);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
- ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
- if (ret)
- goto out;
-
- if ((reg_val & bit_mask) != bit_mask) {
- reg_val |= bit_mask;
- ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
- }
-out:
- mutex_unlock(&tps6586x->lock);
- return ret;
+ return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask);
}
EXPORT_SYMBOL_GPL(tps6586x_set_bits);
int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
{
- struct tps6586x *tps6586x = dev_get_drvdata(dev);
- uint8_t reg_val;
- int ret = 0;
-
- mutex_lock(&tps6586x->lock);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
- ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
- if (ret)
- goto out;
-
- if (reg_val & bit_mask) {
- reg_val &= ~bit_mask;
- ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
- }
-out:
- mutex_unlock(&tps6586x->lock);
- return ret;
+ return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0);
}
EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
{
- struct tps6586x *tps6586x = dev_get_drvdata(dev);
- uint8_t reg_val;
- int ret = 0;
-
- mutex_lock(&tps6586x->lock);
+ struct tps6586x *tps6586x = dev_to_tps6586x(dev);
- ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
- if (ret)
- goto out;
-
- if ((reg_val & mask) != val) {
- reg_val = (reg_val & ~mask) | val;
- ret = __tps6586x_write(tps6586x->client, reg, reg_val);
- }
-out:
- mutex_unlock(&tps6586x->lock);
- return ret;
+ return regmap_update_bits(tps6586x->regmap, reg, mask, val);
}
EXPORT_SYMBOL_GPL(tps6586x_update);
-static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
-{
- struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
- uint8_t val;
- int ret;
-
- ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
- if (ret)
- return ret;
-
- return !!(val & (1 << offset));
-}
-
-
-static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
-
- tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2,
- value << offset, 1 << offset);
-}
-
-static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
- int value)
-{
- struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
- uint8_t val, mask;
-
- tps6586x_gpio_set(gc, offset, value);
-
- val = 0x1 << (offset * 2);
- mask = 0x3 << (offset * 2);
-
- return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
-}
-
-static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
-{
- if (!gpio_base)
- return 0;
-
- tps6586x->gpio.owner = THIS_MODULE;
- tps6586x->gpio.label = tps6586x->client->name;
- tps6586x->gpio.dev = tps6586x->dev;
- tps6586x->gpio.base = gpio_base;
- tps6586x->gpio.ngpio = 4;
- tps6586x->gpio.can_sleep = 1;
-
- /* FIXME: add handling of GPIOs as dedicated inputs */
- tps6586x->gpio.direction_output = tps6586x_gpio_output;
- tps6586x->gpio.set = tps6586x_gpio_set;
- tps6586x->gpio.get = tps6586x_gpio_get;
-
- return gpiochip_add(&tps6586x->gpio);
-}
-
static int __remove_subdev(struct device *dev, void *unused)
{
platform_device_unregister(to_platform_device(dev));
@@ -354,12 +224,11 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)
int i;
for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
- if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
- if (!WARN_ON(tps6586x_write(tps6586x->dev,
- TPS6586X_INT_MASK1 + i,
- tps6586x->mask_reg[i])))
- tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
- }
+ int ret;
+ ret = tps6586x_write(tps6586x->dev,
+ TPS6586X_INT_MASK1 + i,
+ tps6586x->mask_reg[i]);
+ WARN_ON(ret);
}
mutex_unlock(&tps6586x->irq_lock);
@@ -406,7 +275,6 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
mutex_init(&tps6586x->irq_lock);
for (i = 0; i < 5; i++) {
- tps6586x->mask_cache[i] = 0xff;
tps6586x->mask_reg[i] = 0xff;
tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
}
@@ -556,6 +424,23 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
}
#endif
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* Cache all interrupt mask register */
+ if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5))
+ return false;
+
+ return true;
+}
+
+static const struct regmap_config tps6586x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPS6586X_MAX_REGISTER - 1,
+ .volatile_reg = is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -579,29 +464,39 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
dev_info(&client->dev, "VERSIONCRC is %02x\n", ret);
- tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
- if (tps6586x == NULL)
+ tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
+ if (tps6586x == NULL) {
+ dev_err(&client->dev, "memory for tps6586x alloc failed\n");
return -ENOMEM;
+ }
tps6586x->client = client;
tps6586x->dev = &client->dev;
i2c_set_clientdata(client, tps6586x);
- mutex_init(&tps6586x->lock);
+ tps6586x->regmap = devm_regmap_init_i2c(client,
+ &tps6586x_regmap_config);
+ if (IS_ERR(tps6586x->regmap)) {
+ ret = PTR_ERR(tps6586x->regmap);
+ dev_err(&client->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
if (client->irq) {
ret = tps6586x_irq_init(tps6586x, client->irq,
pdata->irq_base);
if (ret) {
dev_err(&client->dev, "IRQ init failed: %d\n", ret);
- goto err_irq_init;
+ return ret;
}
}
- ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base);
- if (ret) {
- dev_err(&client->dev, "GPIO registration failed: %d\n", ret);
- goto err_gpio_init;
+ ret = mfd_add_devices(tps6586x->dev, -1,
+ tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0);
+ if (ret < 0) {
+ dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
+ goto err_mfd_add;
}
ret = tps6586x_add_subdevs(tps6586x, pdata);
@@ -613,38 +508,21 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return 0;
err_add_devs:
- if (pdata->gpio_base) {
- ret = gpiochip_remove(&tps6586x->gpio);
- if (ret)
- dev_err(&client->dev, "Can't remove gpio chip: %d\n",
- ret);
- }
-err_gpio_init:
+ mfd_remove_devices(tps6586x->dev);
+err_mfd_add:
if (client->irq)
free_irq(client->irq, tps6586x);
-err_irq_init:
- kfree(tps6586x);
return ret;
}
static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
{
struct tps6586x *tps6586x = i2c_get_clientdata(client);
- struct tps6586x_platform_data *pdata = client->dev.platform_data;
- int ret;
+ tps6586x_remove_subdevs(tps6586x);
+ mfd_remove_devices(tps6586x->dev);
if (client->irq)
free_irq(client->irq, tps6586x);
-
- if (pdata->gpio_base) {
- ret = gpiochip_remove(&tps6586x->gpio);
- if (ret)
- dev_err(&client->dev, "Can't remove gpio chip: %d\n",
- ret);
- }
-
- tps6586x_remove_subdevs(tps6586x);
- kfree(tps6586x);
return 0;
}
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index be9e07b77325..1c563792c777 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -68,6 +68,24 @@ static const struct regmap_config tps65910_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910,
+ struct tps65910_board *pmic_pdata)
+{
+ int ret;
+
+ if (!pmic_pdata->en_ck32k_xtal)
+ return 0;
+
+ ret = tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
+ DEVCTRL_CK32K_CTRL_MASK);
+ if (ret < 0) {
+ dev_err(tps65910->dev, "clear ck32k_ctrl failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
struct tps65910_board *pmic_pdata)
{
@@ -175,6 +193,9 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
else if (*chip_id == TPS65911)
dev_warn(&client->dev, "VMBCH2-Threshold not specified");
+ prop = of_property_read_bool(np, "ti,en-ck32k-xtal");
+ board_info->en_ck32k_xtal = prop;
+
board_info->irq = client->irq;
board_info->irq_base = -1;
@@ -243,7 +264,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
init_data->irq_base = pmic_plat_data->irq_base;
tps65910_irq_init(tps65910, init_data->irq, init_data);
-
+ tps65910_ck32k_init(tps65910, pmic_plat_data);
tps65910_sleepinit(tps65910, pmic_plat_data);
return ret;
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 6fc90befa79e..1c32afed28aa 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -568,7 +568,6 @@ add_numbered_child(unsigned chip, const char *name, int num,
goto err;
}
- device_init_wakeup(&pdev->dev, can_wakeup);
pdev->dev.parent = &twl->client->dev;
if (pdata) {
@@ -593,6 +592,8 @@ add_numbered_child(unsigned chip, const char *name, int num,
}
status = platform_device_add(pdev);
+ if (status == 0)
+ device_init_wakeup(&pdev->dev, can_wakeup);
err:
if (status < 0) {
@@ -716,8 +717,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
static struct regulator_consumer_supply usb1v8 = {
.supply = "usb1v8",
};
- static struct regulator_consumer_supply usb3v1 = {
- .supply = "usb3v1",
+ static struct regulator_consumer_supply usb3v1[] = {
+ { .supply = "usb3v1" },
+ { .supply = "bci3v1" },
};
/* First add the regulators so that they can be used by transceiver */
@@ -745,7 +747,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
return PTR_ERR(child);
child = add_regulator_linked(TWL4030_REG_VUSB3V1,
- &usb_fixed, &usb3v1, 1,
+ &usb_fixed, usb3v1, 2,
features);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -766,7 +768,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (twl_has_regulator() && child) {
usb1v5.dev_name = dev_name(child);
usb1v8.dev_name = dev_name(child);
- usb3v1.dev_name = dev_name(child);
+ usb3v1[0].dev_name = dev_name(child);
}
}
if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c
index 4ded9e7aa246..b0fad0ffca56 100644
--- a/drivers/mfd/twl6040-core.c
+++ b/drivers/mfd/twl6040-core.c
@@ -64,19 +64,15 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
int ret;
unsigned int val;
- mutex_lock(&twl6040->io_mutex);
/* Vibra control registers from cache */
if (unlikely(reg == TWL6040_REG_VIBCTLL ||
reg == TWL6040_REG_VIBCTLR)) {
val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
} else {
ret = regmap_read(twl6040->regmap, reg, &val);
- if (ret < 0) {
- mutex_unlock(&twl6040->io_mutex);
+ if (ret < 0)
return ret;
- }
}
- mutex_unlock(&twl6040->io_mutex);
return val;
}
@@ -86,12 +82,10 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
{
int ret;
- mutex_lock(&twl6040->io_mutex);
ret = regmap_write(twl6040->regmap, reg, val);
/* Cache the vibra control registers */
if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
- mutex_unlock(&twl6040->io_mutex);
return ret;
}
@@ -99,23 +93,13 @@ EXPORT_SYMBOL(twl6040_reg_write);
int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
{
- int ret;
-
- mutex_lock(&twl6040->io_mutex);
- ret = regmap_update_bits(twl6040->regmap, reg, mask, mask);
- mutex_unlock(&twl6040->io_mutex);
- return ret;
+ return regmap_update_bits(twl6040->regmap, reg, mask, mask);
}
EXPORT_SYMBOL(twl6040_set_bits);
int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
{
- int ret;
-
- mutex_lock(&twl6040->io_mutex);
- ret = regmap_update_bits(twl6040->regmap, reg, mask, 0);
- mutex_unlock(&twl6040->io_mutex);
- return ret;
+ return regmap_update_bits(twl6040->regmap, reg, mask, 0);
}
EXPORT_SYMBOL(twl6040_clear_bits);
@@ -573,7 +557,6 @@ static int __devinit twl6040_probe(struct i2c_client *client,
twl6040->irq = client->irq;
mutex_init(&twl6040->mutex);
- mutex_init(&twl6040->io_mutex);
init_completion(&twl6040->ready);
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
@@ -696,6 +679,7 @@ static int __devexit twl6040_remove(struct i2c_client *client)
static const struct i2c_device_id twl6040_i2c_id[] = {
{ "twl6040", 0, },
+ { "twl6041", 0, },
{ },
};
MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
new file mode 100644
index 000000000000..01b9255ed631
--- /dev/null
+++ b/drivers/mfd/wm5102-tables.c
@@ -0,0 +1,2399 @@
+/*
+ * wm5102-tables.c -- WM5102 data tables
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define WM5102_NUM_AOD_ISR 2
+#define WM5102_NUM_ISR 5
+
+static const struct reg_default wm5102_reva_patch[] = {
+ { 0x80, 0x0003 },
+ { 0x221, 0x0090 },
+ { 0x211, 0x0014 },
+ { 0x212, 0x0000 },
+ { 0x214, 0x000C },
+ { 0x171, 0x0002 },
+ { 0x171, 0x0000 },
+ { 0x461, 0x8000 },
+ { 0x463, 0x50F0 },
+ { 0x465, 0x4820 },
+ { 0x467, 0x4040 },
+ { 0x469, 0x3940 },
+ { 0x46B, 0x3310 },
+ { 0x46D, 0x2D80 },
+ { 0x46F, 0x2890 },
+ { 0x471, 0x1990 },
+ { 0x473, 0x1450 },
+ { 0x475, 0x1020 },
+ { 0x477, 0x0CD0 },
+ { 0x479, 0x0A30 },
+ { 0x47B, 0x0810 },
+ { 0x47D, 0x0510 },
+ { 0x500, 0x000D },
+ { 0x507, 0x1820 },
+ { 0x508, 0x1820 },
+ { 0x540, 0x000D },
+ { 0x547, 0x1820 },
+ { 0x548, 0x1820 },
+ { 0x580, 0x000D },
+ { 0x587, 0x1820 },
+ { 0x588, 0x1820 },
+ { 0x101, 0x8140 },
+ { 0x3000, 0x2225 },
+ { 0x3001, 0x3a03 },
+ { 0x3002, 0x0225 },
+ { 0x3003, 0x0801 },
+ { 0x3004, 0x6249 },
+ { 0x3005, 0x0c04 },
+ { 0x3006, 0x0225 },
+ { 0x3007, 0x5901 },
+ { 0x3008, 0xe249 },
+ { 0x3009, 0x030d },
+ { 0x300a, 0x0249 },
+ { 0x300b, 0x2c01 },
+ { 0x300c, 0xe249 },
+ { 0x300d, 0x4342 },
+ { 0x300e, 0xe249 },
+ { 0x300f, 0x73c0 },
+ { 0x3010, 0x4249 },
+ { 0x3011, 0x0c00 },
+ { 0x3012, 0x0225 },
+ { 0x3013, 0x1f01 },
+ { 0x3014, 0x0225 },
+ { 0x3015, 0x1e01 },
+ { 0x3016, 0x0225 },
+ { 0x3017, 0xfa00 },
+ { 0x3018, 0x0000 },
+ { 0x3019, 0xf000 },
+ { 0x301a, 0x0000 },
+ { 0x301b, 0xf000 },
+ { 0x301c, 0x0000 },
+ { 0x301d, 0xf000 },
+ { 0x301e, 0x0000 },
+ { 0x301f, 0xf000 },
+ { 0x3020, 0x0000 },
+ { 0x3021, 0xf000 },
+ { 0x3022, 0x0000 },
+ { 0x3023, 0xf000 },
+ { 0x3024, 0x0000 },
+ { 0x3025, 0xf000 },
+ { 0x3026, 0x0000 },
+ { 0x3027, 0xf000 },
+ { 0x3028, 0x0000 },
+ { 0x3029, 0xf000 },
+ { 0x302a, 0x0000 },
+ { 0x302b, 0xf000 },
+ { 0x302c, 0x0000 },
+ { 0x302d, 0xf000 },
+ { 0x302e, 0x0000 },
+ { 0x302f, 0xf000 },
+ { 0x3030, 0x0225 },
+ { 0x3031, 0x1a01 },
+ { 0x3032, 0x0225 },
+ { 0x3033, 0x1e00 },
+ { 0x3034, 0x0225 },
+ { 0x3035, 0x1f00 },
+ { 0x3036, 0x6225 },
+ { 0x3037, 0xf800 },
+ { 0x3038, 0x0000 },
+ { 0x3039, 0xf000 },
+ { 0x303a, 0x0000 },
+ { 0x303b, 0xf000 },
+ { 0x303c, 0x0000 },
+ { 0x303d, 0xf000 },
+ { 0x303e, 0x0000 },
+ { 0x303f, 0xf000 },
+ { 0x3040, 0x2226 },
+ { 0x3041, 0x3a03 },
+ { 0x3042, 0x0226 },
+ { 0x3043, 0x0801 },
+ { 0x3044, 0x6249 },
+ { 0x3045, 0x0c06 },
+ { 0x3046, 0x0226 },
+ { 0x3047, 0x5901 },
+ { 0x3048, 0xe249 },
+ { 0x3049, 0x030d },
+ { 0x304a, 0x0249 },
+ { 0x304b, 0x2c01 },
+ { 0x304c, 0xe249 },
+ { 0x304d, 0x4342 },
+ { 0x304e, 0xe249 },
+ { 0x304f, 0x73c0 },
+ { 0x3050, 0x4249 },
+ { 0x3051, 0x0c00 },
+ { 0x3052, 0x0226 },
+ { 0x3053, 0x1f01 },
+ { 0x3054, 0x0226 },
+ { 0x3055, 0x1e01 },
+ { 0x3056, 0x0226 },
+ { 0x3057, 0xfa00 },
+ { 0x3058, 0x0000 },
+ { 0x3059, 0xf000 },
+ { 0x305a, 0x0000 },
+ { 0x305b, 0xf000 },
+ { 0x305c, 0x0000 },
+ { 0x305d, 0xf000 },
+ { 0x305e, 0x0000 },
+ { 0x305f, 0xf000 },
+ { 0x3060, 0x0000 },
+ { 0x3061, 0xf000 },
+ { 0x3062, 0x0000 },
+ { 0x3063, 0xf000 },
+ { 0x3064, 0x0000 },
+ { 0x3065, 0xf000 },
+ { 0x3066, 0x0000 },
+ { 0x3067, 0xf000 },
+ { 0x3068, 0x0000 },
+ { 0x3069, 0xf000 },
+ { 0x306a, 0x0000 },
+ { 0x306b, 0xf000 },
+ { 0x306c, 0x0000 },
+ { 0x306d, 0xf000 },
+ { 0x306e, 0x0000 },
+ { 0x306f, 0xf000 },
+ { 0x3070, 0x0226 },
+ { 0x3071, 0x1a01 },
+ { 0x3072, 0x0226 },
+ { 0x3073, 0x1e00 },
+ { 0x3074, 0x0226 },
+ { 0x3075, 0x1f00 },
+ { 0x3076, 0x6226 },
+ { 0x3077, 0xf800 },
+ { 0x3078, 0x0000 },
+ { 0x3079, 0xf000 },
+ { 0x307a, 0x0000 },
+ { 0x307b, 0xf000 },
+ { 0x307c, 0x0000 },
+ { 0x307d, 0xf000 },
+ { 0x307e, 0x0000 },
+ { 0x307f, 0xf000 },
+ { 0x3080, 0x2227 },
+ { 0x3081, 0x3a03 },
+ { 0x3082, 0x0227 },
+ { 0x3083, 0x0801 },
+ { 0x3084, 0x6255 },
+ { 0x3085, 0x0c04 },
+ { 0x3086, 0x0227 },
+ { 0x3087, 0x5901 },
+ { 0x3088, 0xe255 },
+ { 0x3089, 0x030d },
+ { 0x308a, 0x0255 },
+ { 0x308b, 0x2c01 },
+ { 0x308c, 0xe255 },
+ { 0x308d, 0x4342 },
+ { 0x308e, 0xe255 },
+ { 0x308f, 0x73c0 },
+ { 0x3090, 0x4255 },
+ { 0x3091, 0x0c00 },
+ { 0x3092, 0x0227 },
+ { 0x3093, 0x1f01 },
+ { 0x3094, 0x0227 },
+ { 0x3095, 0x1e01 },
+ { 0x3096, 0x0227 },
+ { 0x3097, 0xfa00 },
+ { 0x3098, 0x0000 },
+ { 0x3099, 0xf000 },
+ { 0x309a, 0x0000 },
+ { 0x309b, 0xf000 },
+ { 0x309c, 0x0000 },
+ { 0x309d, 0xf000 },
+ { 0x309e, 0x0000 },
+ { 0x309f, 0xf000 },
+ { 0x30a0, 0x0000 },
+ { 0x30a1, 0xf000 },
+ { 0x30a2, 0x0000 },
+ { 0x30a3, 0xf000 },
+ { 0x30a4, 0x0000 },
+ { 0x30a5, 0xf000 },
+ { 0x30a6, 0x0000 },
+ { 0x30a7, 0xf000 },
+ { 0x30a8, 0x0000 },
+ { 0x30a9, 0xf000 },
+ { 0x30aa, 0x0000 },
+ { 0x30ab, 0xf000 },
+ { 0x30ac, 0x0000 },
+ { 0x30ad, 0xf000 },
+ { 0x30ae, 0x0000 },
+ { 0x30af, 0xf000 },
+ { 0x30b0, 0x0227 },
+ { 0x30b1, 0x1a01 },
+ { 0x30b2, 0x0227 },
+ { 0x30b3, 0x1e00 },
+ { 0x30b4, 0x0227 },
+ { 0x30b5, 0x1f00 },
+ { 0x30b6, 0x6227 },
+ { 0x30b7, 0xf800 },
+ { 0x30b8, 0x0000 },
+ { 0x30b9, 0xf000 },
+ { 0x30ba, 0x0000 },
+ { 0x30bb, 0xf000 },
+ { 0x30bc, 0x0000 },
+ { 0x30bd, 0xf000 },
+ { 0x30be, 0x0000 },
+ { 0x30bf, 0xf000 },
+ { 0x30c0, 0x2228 },
+ { 0x30c1, 0x3a03 },
+ { 0x30c2, 0x0228 },
+ { 0x30c3, 0x0801 },
+ { 0x30c4, 0x6255 },
+ { 0x30c5, 0x0c06 },
+ { 0x30c6, 0x0228 },
+ { 0x30c7, 0x5901 },
+ { 0x30c8, 0xe255 },
+ { 0x30c9, 0x030d },
+ { 0x30ca, 0x0255 },
+ { 0x30cb, 0x2c01 },
+ { 0x30cc, 0xe255 },
+ { 0x30cd, 0x4342 },
+ { 0x30ce, 0xe255 },
+ { 0x30cf, 0x73c0 },
+ { 0x30d0, 0x4255 },
+ { 0x30d1, 0x0c00 },
+ { 0x30d2, 0x0228 },
+ { 0x30d3, 0x1f01 },
+ { 0x30d4, 0x0228 },
+ { 0x30d5, 0x1e01 },
+ { 0x30d6, 0x0228 },
+ { 0x30d7, 0xfa00 },
+ { 0x30d8, 0x0000 },
+ { 0x30d9, 0xf000 },
+ { 0x30da, 0x0000 },
+ { 0x30db, 0xf000 },
+ { 0x30dc, 0x0000 },
+ { 0x30dd, 0xf000 },
+ { 0x30de, 0x0000 },
+ { 0x30df, 0xf000 },
+ { 0x30e0, 0x0000 },
+ { 0x30e1, 0xf000 },
+ { 0x30e2, 0x0000 },
+ { 0x30e3, 0xf000 },
+ { 0x30e4, 0x0000 },
+ { 0x30e5, 0xf000 },
+ { 0x30e6, 0x0000 },
+ { 0x30e7, 0xf000 },
+ { 0x30e8, 0x0000 },
+ { 0x30e9, 0xf000 },
+ { 0x30ea, 0x0000 },
+ { 0x30eb, 0xf000 },
+ { 0x30ec, 0x0000 },
+ { 0x30ed, 0xf000 },
+ { 0x30ee, 0x0000 },
+ { 0x30ef, 0xf000 },
+ { 0x30f0, 0x0228 },
+ { 0x30f1, 0x1a01 },
+ { 0x30f2, 0x0228 },
+ { 0x30f3, 0x1e00 },
+ { 0x30f4, 0x0228 },
+ { 0x30f5, 0x1f00 },
+ { 0x30f6, 0x6228 },
+ { 0x30f7, 0xf800 },
+ { 0x30f8, 0x0000 },
+ { 0x30f9, 0xf000 },
+ { 0x30fa, 0x0000 },
+ { 0x30fb, 0xf000 },
+ { 0x30fc, 0x0000 },
+ { 0x30fd, 0xf000 },
+ { 0x30fe, 0x0000 },
+ { 0x30ff, 0xf000 },
+ { 0x3100, 0x222b },
+ { 0x3101, 0x3a03 },
+ { 0x3102, 0x222b },
+ { 0x3103, 0x5803 },
+ { 0x3104, 0xe26f },
+ { 0x3105, 0x030d },
+ { 0x3106, 0x626f },
+ { 0x3107, 0x2c01 },
+ { 0x3108, 0xe26f },
+ { 0x3109, 0x4342 },
+ { 0x310a, 0xe26f },
+ { 0x310b, 0x73c0 },
+ { 0x310c, 0x026f },
+ { 0x310d, 0x0c00 },
+ { 0x310e, 0x022b },
+ { 0x310f, 0x1f01 },
+ { 0x3110, 0x022b },
+ { 0x3111, 0x1e01 },
+ { 0x3112, 0x022b },
+ { 0x3113, 0xfa00 },
+ { 0x3114, 0x0000 },
+ { 0x3115, 0xf000 },
+ { 0x3116, 0x0000 },
+ { 0x3117, 0xf000 },
+ { 0x3118, 0x0000 },
+ { 0x3119, 0xf000 },
+ { 0x311a, 0x0000 },
+ { 0x311b, 0xf000 },
+ { 0x311c, 0x0000 },
+ { 0x311d, 0xf000 },
+ { 0x311e, 0x0000 },
+ { 0x311f, 0xf000 },
+ { 0x3120, 0x022b },
+ { 0x3121, 0x0a01 },
+ { 0x3122, 0x022b },
+ { 0x3123, 0x1e00 },
+ { 0x3124, 0x022b },
+ { 0x3125, 0x1f00 },
+ { 0x3126, 0x622b },
+ { 0x3127, 0xf800 },
+ { 0x3128, 0x0000 },
+ { 0x3129, 0xf000 },
+ { 0x312a, 0x0000 },
+ { 0x312b, 0xf000 },
+ { 0x312c, 0x0000 },
+ { 0x312d, 0xf000 },
+ { 0x312e, 0x0000 },
+ { 0x312f, 0xf000 },
+ { 0x3130, 0x0000 },
+ { 0x3131, 0xf000 },
+ { 0x3132, 0x0000 },
+ { 0x3133, 0xf000 },
+ { 0x3134, 0x0000 },
+ { 0x3135, 0xf000 },
+ { 0x3136, 0x0000 },
+ { 0x3137, 0xf000 },
+ { 0x3138, 0x0000 },
+ { 0x3139, 0xf000 },
+ { 0x313a, 0x0000 },
+ { 0x313b, 0xf000 },
+ { 0x313c, 0x0000 },
+ { 0x313d, 0xf000 },
+ { 0x313e, 0x0000 },
+ { 0x313f, 0xf000 },
+ { 0x3140, 0x0000 },
+ { 0x3141, 0xf000 },
+ { 0x3142, 0x0000 },
+ { 0x3143, 0xf000 },
+ { 0x3144, 0x0000 },
+ { 0x3145, 0xf000 },
+ { 0x3146, 0x0000 },
+ { 0x3147, 0xf000 },
+ { 0x3148, 0x0000 },
+ { 0x3149, 0xf000 },
+ { 0x314a, 0x0000 },
+ { 0x314b, 0xf000 },
+ { 0x314c, 0x0000 },
+ { 0x314d, 0xf000 },
+ { 0x314e, 0x0000 },
+ { 0x314f, 0xf000 },
+ { 0x3150, 0x0000 },
+ { 0x3151, 0xf000 },
+ { 0x3152, 0x0000 },
+ { 0x3153, 0xf000 },
+ { 0x3154, 0x0000 },
+ { 0x3155, 0xf000 },
+ { 0x3156, 0x0000 },
+ { 0x3157, 0xf000 },
+ { 0x3158, 0x0000 },
+ { 0x3159, 0xf000 },
+ { 0x315a, 0x0000 },
+ { 0x315b, 0xf000 },
+ { 0x315c, 0x0000 },
+ { 0x315d, 0xf000 },
+ { 0x315e, 0x0000 },
+ { 0x315f, 0xf000 },
+ { 0x3160, 0x0000 },
+ { 0x3161, 0xf000 },
+ { 0x3162, 0x0000 },
+ { 0x3163, 0xf000 },
+ { 0x3164, 0x0000 },
+ { 0x3165, 0xf000 },
+ { 0x3166, 0x0000 },
+ { 0x3167, 0xf000 },
+ { 0x3168, 0x0000 },
+ { 0x3169, 0xf000 },
+ { 0x316a, 0x0000 },
+ { 0x316b, 0xf000 },
+ { 0x316c, 0x0000 },
+ { 0x316d, 0xf000 },
+ { 0x316e, 0x0000 },
+ { 0x316f, 0xf000 },
+ { 0x3170, 0x0000 },
+ { 0x3171, 0xf000 },
+ { 0x3172, 0x0000 },
+ { 0x3173, 0xf000 },
+ { 0x3174, 0x0000 },
+ { 0x3175, 0xf000 },
+ { 0x3176, 0x0000 },
+ { 0x3177, 0xf000 },
+ { 0x3178, 0x0000 },
+ { 0x3179, 0xf000 },
+ { 0x317a, 0x0000 },
+ { 0x317b, 0xf000 },
+ { 0x317c, 0x0000 },
+ { 0x317d, 0xf000 },
+ { 0x317e, 0x0000 },
+ { 0x317f, 0xf000 },
+ { 0x3180, 0x2001 },
+ { 0x3181, 0xf101 },
+ { 0x3182, 0x0000 },
+ { 0x3183, 0xf000 },
+ { 0x3184, 0x0000 },
+ { 0x3185, 0xf000 },
+ { 0x3186, 0x0000 },
+ { 0x3187, 0xf000 },
+ { 0x3188, 0x0000 },
+ { 0x3189, 0xf000 },
+ { 0x318a, 0x0000 },
+ { 0x318b, 0xf000 },
+ { 0x318c, 0x0000 },
+ { 0x318d, 0xf000 },
+ { 0x318e, 0x0000 },
+ { 0x318f, 0xf000 },
+ { 0x3190, 0x0000 },
+ { 0x3191, 0xf000 },
+ { 0x3192, 0x0000 },
+ { 0x3193, 0xf000 },
+ { 0x3194, 0x0000 },
+ { 0x3195, 0xf000 },
+ { 0x3196, 0x0000 },
+ { 0x3197, 0xf000 },
+ { 0x3198, 0x0000 },
+ { 0x3199, 0xf000 },
+ { 0x319a, 0x0000 },
+ { 0x319b, 0xf000 },
+ { 0x319c, 0x0000 },
+ { 0x319d, 0xf000 },
+ { 0x319e, 0x0000 },
+ { 0x319f, 0xf000 },
+ { 0x31a0, 0x0000 },
+ { 0x31a1, 0xf000 },
+ { 0x31a2, 0x0000 },
+ { 0x31a3, 0xf000 },
+ { 0x31a4, 0x0000 },
+ { 0x31a5, 0xf000 },
+ { 0x31a6, 0x0000 },
+ { 0x31a7, 0xf000 },
+ { 0x31a8, 0x0000 },
+ { 0x31a9, 0xf000 },
+ { 0x31aa, 0x0000 },
+ { 0x31ab, 0xf000 },
+ { 0x31ac, 0x0000 },
+ { 0x31ad, 0xf000 },
+ { 0x31ae, 0x0000 },
+ { 0x31af, 0xf000 },
+ { 0x31b0, 0x0000 },
+ { 0x31b1, 0xf000 },
+ { 0x31b2, 0x0000 },
+ { 0x31b3, 0xf000 },
+ { 0x31b4, 0x0000 },
+ { 0x31b5, 0xf000 },
+ { 0x31b6, 0x0000 },
+ { 0x31b7, 0xf000 },
+ { 0x31b8, 0x0000 },
+ { 0x31b9, 0xf000 },
+ { 0x31ba, 0x0000 },
+ { 0x31bb, 0xf000 },
+ { 0x31bc, 0x0000 },
+ { 0x31bd, 0xf000 },
+ { 0x31be, 0x0000 },
+ { 0x31bf, 0xf000 },
+ { 0x31c0, 0x0000 },
+ { 0x31c1, 0xf000 },
+ { 0x31c2, 0x0000 },
+ { 0x31c3, 0xf000 },
+ { 0x31c4, 0x0000 },
+ { 0x31c5, 0xf000 },
+ { 0x31c6, 0x0000 },
+ { 0x31c7, 0xf000 },
+ { 0x31c8, 0x0000 },
+ { 0x31c9, 0xf000 },
+ { 0x31ca, 0x0000 },
+ { 0x31cb, 0xf000 },
+ { 0x31cc, 0x0000 },
+ { 0x31cd, 0xf000 },
+ { 0x31ce, 0x0000 },
+ { 0x31cf, 0xf000 },
+ { 0x31d0, 0x0000 },
+ { 0x31d1, 0xf000 },
+ { 0x31d2, 0x0000 },
+ { 0x31d3, 0xf000 },
+ { 0x31d4, 0x0000 },
+ { 0x31d5, 0xf000 },
+ { 0x31d6, 0x0000 },
+ { 0x31d7, 0xf000 },
+ { 0x31d8, 0x0000 },
+ { 0x31d9, 0xf000 },
+ { 0x31da, 0x0000 },
+ { 0x31db, 0xf000 },
+ { 0x31dc, 0x0000 },
+ { 0x31dd, 0xf000 },
+ { 0x31de, 0x0000 },
+ { 0x31df, 0xf000 },
+ { 0x31e0, 0x0000 },
+ { 0x31e1, 0xf000 },
+ { 0x31e2, 0x0000 },
+ { 0x31e3, 0xf000 },
+ { 0x31e4, 0x0000 },
+ { 0x31e5, 0xf000 },
+ { 0x31e6, 0x0000 },
+ { 0x31e7, 0xf000 },
+ { 0x31e8, 0x0000 },
+ { 0x31e9, 0xf000 },
+ { 0x31ea, 0x0000 },
+ { 0x31eb, 0xf000 },
+ { 0x31ec, 0x0000 },
+ { 0x31ed, 0xf000 },
+ { 0x31ee, 0x0000 },
+ { 0x31ef, 0xf000 },
+ { 0x31f0, 0x0000 },
+ { 0x31f1, 0xf000 },
+ { 0x31f2, 0x0000 },
+ { 0x31f3, 0xf000 },
+ { 0x31f4, 0x0000 },
+ { 0x31f5, 0xf000 },
+ { 0x31f6, 0x0000 },
+ { 0x31f7, 0xf000 },
+ { 0x31f8, 0x0000 },
+ { 0x31f9, 0xf000 },
+ { 0x31fa, 0x0000 },
+ { 0x31fb, 0xf000 },
+ { 0x31fc, 0x0000 },
+ { 0x31fd, 0xf000 },
+ { 0x31fe, 0x0000 },
+ { 0x31ff, 0xf000 },
+ { 0x024d, 0xff50 },
+ { 0x0252, 0xff50 },
+ { 0x0259, 0x0112 },
+ { 0x025e, 0x0112 },
+ { 0x101, 0x0304 },
+ { 0x80, 0x0000 },
+};
+
+/* We use a function so we can use ARRAY_SIZE() */
+int wm5102_patch(struct arizona *arizona)
+{
+ switch (arizona->rev) {
+ case 0:
+ return regmap_register_patch(arizona->regmap,
+ wm5102_reva_patch,
+ ARRAY_SIZE(wm5102_reva_patch));
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
+ [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
+ [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
+ [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 },
+};
+
+const struct regmap_irq_chip wm5102_aod = {
+ .name = "wm5102 AOD",
+ .status_base = ARIZONA_AOD_IRQ1,
+ .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
+ .ack_base = ARIZONA_AOD_IRQ1,
+ .wake_base = ARIZONA_WAKE_CONTROL,
+ .num_regs = 1,
+ .irqs = wm5102_aod_irqs,
+ .num_irqs = ARRAY_SIZE(wm5102_aod_irqs),
+};
+
+static const struct regmap_irq wm5102_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 },
+ [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 },
+ [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+ [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+ [ARIZONA_IRQ_DSP1_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ2] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ1] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1
+ },
+
+ [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1
+ },
+ [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+ },
+ [ARIZONA_IRQ_HPDET] = {
+ .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1
+ },
+ [ARIZONA_IRQ_MICDET] = {
+ .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1
+ },
+ [ARIZONA_IRQ_WSEQ_DONE] = {
+ .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DRC2_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_DRC1_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_ASRC2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_ASRC1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_UNDERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_OVERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+ },
+
+ [ARIZONA_IRQ_ASRC_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF3_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF2_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF1_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CTRLIF_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1
+ },
+ [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+ .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1
+ },
+ [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1
+ },
+
+ [ARIZONA_IRQ_BOOT_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_DAC_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_HP_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+ },
+};
+
+const struct regmap_irq_chip wm5102_irq = {
+ .name = "wm5102 IRQ",
+ .status_base = ARIZONA_INTERRUPT_STATUS_1,
+ .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+ .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+ .num_regs = 5,
+ .irqs = wm5102_irqs,
+ .num_irqs = ARRAY_SIZE(wm5102_irqs),
+};
+
+static const struct reg_default wm5102_reg_default[] = {
+ { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
+ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
+ { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */
+ { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
+ { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
+ { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
+ { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */
+ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
+ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
+ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
+ { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */
+ { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */
+ { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */
+ { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */
+ { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */
+ { 0x00000040, 0x0000 }, /* R64 - Wake control */
+ { 0x00000041, 0x0000 }, /* R65 - Sequence control */
+ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */
+ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
+ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
+ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
+ { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */
+ { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */
+ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
+ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
+ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
+ { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */
+ { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */
+ { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */
+ { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
+ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
+ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
+ { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */
+ { 0x00000101, 0x0304 }, /* R257 - System Clock 1 */
+ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
+ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
+ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
+ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
+ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000149, 0x0000 }, /* R329 - Output system clock */
+ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */
+ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
+ { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */
+ { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
+ { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
+ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
+ { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
+ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
+ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
+ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
+ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */
+ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
+ { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
+ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
+ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
+ { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
+ { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
+ { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
+ { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
+ { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
+ { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */
+ { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */
+ { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */
+ { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */
+ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
+ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
+ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
+ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
+ { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
+ { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
+ { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
+ { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
+ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
+ { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */
+ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
+ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
+ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
+ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
+ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
+ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
+ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
+ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
+ { 0x000002CB, 0x0000 }, /* R715 - Isolation control */
+ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
+ { 0x00000300, 0x0000 }, /* R768 - Input Enables */
+ { 0x00000308, 0x0000 }, /* R776 - Input Rate */
+ { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */
+ { 0x00000310, 0x2080 }, /* R784 - IN1L Control */
+ { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */
+ { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */
+ { 0x00000314, 0x0080 }, /* R788 - IN1R Control */
+ { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */
+ { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */
+ { 0x00000318, 0x2080 }, /* R792 - IN2L Control */
+ { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */
+ { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */
+ { 0x0000031C, 0x0080 }, /* R796 - IN2R Control */
+ { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */
+ { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */
+ { 0x00000320, 0x2080 }, /* R800 - IN3L Control */
+ { 0x00000321, 0x0180 }, /* R801 - ADC Digital Volume 3L */
+ { 0x00000322, 0x0000 }, /* R802 - DMIC3L Control */
+ { 0x00000324, 0x0080 }, /* R804 - IN3R Control */
+ { 0x00000325, 0x0180 }, /* R805 - ADC Digital Volume 3R */
+ { 0x00000326, 0x0000 }, /* R806 - DMIC3R Control */
+ { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
+ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
+ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
+ { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
+ { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
+ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
+ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
+ { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
+ { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */
+ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */
+ { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */
+ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */
+ { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */
+ { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */
+ { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */
+ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */
+ { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
+ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
+ { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
+ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
+ { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */
+ { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */
+ { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */
+ { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
+ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
+ { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
+ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
+ { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */
+ { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */
+ { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */
+ { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */
+ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
+ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
+ { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
+ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
+ { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */
+ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
+ { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
+ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
+ { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
+ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
+ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
+ { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */
+ { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */
+ { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */
+ { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */
+ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
+ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
+ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
+ { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */
+ { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */
+ { 0x00000505, 0x0040 }, /* R1285 - AIF1 Tx BCLK Rate */
+ { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */
+ { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */
+ { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */
+ { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */
+ { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */
+ { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */
+ { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */
+ { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */
+ { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */
+ { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */
+ { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */
+ { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */
+ { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */
+ { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */
+ { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */
+ { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */
+ { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */
+ { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */
+ { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
+ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
+ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
+ { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */
+ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
+ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
+ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
+ { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */
+ { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */
+ { 0x00000545, 0x0040 }, /* R1349 - AIF2 Tx BCLK Rate */
+ { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */
+ { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */
+ { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */
+ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */
+ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */
+ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */
+ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
+ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
+ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
+ { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */
+ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
+ { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
+ { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
+ { 0x00000583, 0x0000 }, /* R1411 - AIF3 Rate Ctrl */
+ { 0x00000584, 0x0000 }, /* R1412 - AIF3 Format */
+ { 0x00000585, 0x0040 }, /* R1413 - AIF3 Tx BCLK Rate */
+ { 0x00000586, 0x0040 }, /* R1414 - AIF3 Rx BCLK Rate */
+ { 0x00000587, 0x1818 }, /* R1415 - AIF3 Frame Ctrl 1 */
+ { 0x00000588, 0x1818 }, /* R1416 - AIF3 Frame Ctrl 2 */
+ { 0x00000589, 0x0000 }, /* R1417 - AIF3 Frame Ctrl 3 */
+ { 0x0000058A, 0x0001 }, /* R1418 - AIF3 Frame Ctrl 4 */
+ { 0x00000591, 0x0000 }, /* R1425 - AIF3 Frame Ctrl 11 */
+ { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
+ { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
+ { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
+ { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */
+ { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
+ { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
+ { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
+ { 0x000005E7, 0x0000 }, /* R1511 - SLIMbus Rates 3 */
+ { 0x000005E8, 0x0000 }, /* R1512 - SLIMbus Rates 4 */
+ { 0x000005E9, 0x0000 }, /* R1513 - SLIMbus Rates 5 */
+ { 0x000005EA, 0x0000 }, /* R1514 - SLIMbus Rates 6 */
+ { 0x000005EB, 0x0000 }, /* R1515 - SLIMbus Rates 7 */
+ { 0x000005EC, 0x0000 }, /* R1516 - SLIMbus Rates 8 */
+ { 0x000005F5, 0x0000 }, /* R1525 - SLIMbus RX Channel Enable */
+ { 0x000005F6, 0x0000 }, /* R1526 - SLIMbus TX Channel Enable */
+ { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */
+ { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */
+ { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */
+ { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */
+ { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */
+ { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */
+ { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */
+ { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */
+ { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */
+ { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */
+ { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */
+ { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */
+ { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */
+ { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */
+ { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */
+ { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */
+ { 0x00000660, 0x0000 }, /* R1632 - MICMIX Input 1 Source */
+ { 0x00000661, 0x0080 }, /* R1633 - MICMIX Input 1 Volume */
+ { 0x00000662, 0x0000 }, /* R1634 - MICMIX Input 2 Source */
+ { 0x00000663, 0x0080 }, /* R1635 - MICMIX Input 2 Volume */
+ { 0x00000664, 0x0000 }, /* R1636 - MICMIX Input 3 Source */
+ { 0x00000665, 0x0080 }, /* R1637 - MICMIX Input 3 Volume */
+ { 0x00000666, 0x0000 }, /* R1638 - MICMIX Input 4 Source */
+ { 0x00000667, 0x0080 }, /* R1639 - MICMIX Input 4 Volume */
+ { 0x00000668, 0x0000 }, /* R1640 - NOISEMIX Input 1 Source */
+ { 0x00000669, 0x0080 }, /* R1641 - NOISEMIX Input 1 Volume */
+ { 0x0000066A, 0x0000 }, /* R1642 - NOISEMIX Input 2 Source */
+ { 0x0000066B, 0x0080 }, /* R1643 - NOISEMIX Input 2 Volume */
+ { 0x0000066C, 0x0000 }, /* R1644 - NOISEMIX Input 3 Source */
+ { 0x0000066D, 0x0080 }, /* R1645 - NOISEMIX Input 3 Volume */
+ { 0x0000066E, 0x0000 }, /* R1646 - NOISEMIX Input 4 Source */
+ { 0x0000066F, 0x0080 }, /* R1647 - NOISEMIX Input 4 Volume */
+ { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */
+ { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */
+ { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */
+ { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */
+ { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */
+ { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */
+ { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */
+ { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */
+ { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */
+ { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */
+ { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */
+ { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */
+ { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */
+ { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */
+ { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */
+ { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */
+ { 0x00000690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */
+ { 0x00000691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */
+ { 0x00000692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */
+ { 0x00000693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */
+ { 0x00000694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */
+ { 0x00000695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */
+ { 0x00000696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */
+ { 0x00000697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */
+ { 0x00000698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */
+ { 0x00000699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */
+ { 0x0000069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */
+ { 0x0000069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */
+ { 0x0000069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */
+ { 0x0000069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */
+ { 0x0000069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */
+ { 0x0000069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */
+ { 0x000006A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */
+ { 0x000006A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */
+ { 0x000006A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */
+ { 0x000006A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */
+ { 0x000006A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */
+ { 0x000006A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */
+ { 0x000006A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */
+ { 0x000006A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */
+ { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */
+ { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */
+ { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */
+ { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */
+ { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */
+ { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */
+ { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */
+ { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */
+ { 0x000006B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */
+ { 0x000006B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */
+ { 0x000006BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */
+ { 0x000006BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */
+ { 0x000006BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */
+ { 0x000006BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */
+ { 0x000006BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */
+ { 0x000006BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */
+ { 0x000006C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */
+ { 0x000006C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */
+ { 0x000006C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */
+ { 0x000006C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */
+ { 0x000006C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */
+ { 0x000006C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */
+ { 0x000006C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */
+ { 0x000006C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */
+ { 0x000006C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */
+ { 0x000006C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */
+ { 0x000006CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */
+ { 0x000006CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */
+ { 0x000006CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */
+ { 0x000006CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */
+ { 0x000006CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */
+ { 0x000006CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */
+ { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */
+ { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */
+ { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */
+ { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */
+ { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */
+ { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */
+ { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */
+ { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */
+ { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */
+ { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */
+ { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */
+ { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */
+ { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */
+ { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */
+ { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */
+ { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */
+ { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */
+ { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */
+ { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */
+ { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */
+ { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */
+ { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */
+ { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */
+ { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */
+ { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */
+ { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */
+ { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */
+ { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */
+ { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */
+ { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */
+ { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */
+ { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */
+ { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */
+ { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */
+ { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */
+ { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */
+ { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */
+ { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */
+ { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */
+ { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */
+ { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */
+ { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */
+ { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */
+ { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */
+ { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */
+ { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */
+ { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */
+ { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */
+ { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */
+ { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */
+ { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */
+ { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */
+ { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */
+ { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */
+ { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */
+ { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */
+ { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */
+ { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */
+ { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */
+ { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */
+ { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */
+ { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */
+ { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */
+ { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */
+ { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */
+ { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */
+ { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */
+ { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */
+ { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */
+ { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */
+ { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */
+ { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */
+ { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */
+ { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */
+ { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */
+ { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */
+ { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */
+ { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
+ { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
+ { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */
+ { 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */
+ { 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */
+ { 0x00000783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */
+ { 0x00000784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */
+ { 0x00000785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */
+ { 0x00000786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */
+ { 0x00000787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */
+ { 0x00000788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */
+ { 0x00000789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */
+ { 0x0000078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */
+ { 0x0000078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */
+ { 0x0000078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */
+ { 0x0000078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */
+ { 0x0000078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */
+ { 0x0000078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */
+ { 0x000007C0, 0x0000 }, /* R1984 - SLIMTX1MIX Input 1 Source */
+ { 0x000007C1, 0x0080 }, /* R1985 - SLIMTX1MIX Input 1 Volume */
+ { 0x000007C2, 0x0000 }, /* R1986 - SLIMTX1MIX Input 2 Source */
+ { 0x000007C3, 0x0080 }, /* R1987 - SLIMTX1MIX Input 2 Volume */
+ { 0x000007C4, 0x0000 }, /* R1988 - SLIMTX1MIX Input 3 Source */
+ { 0x000007C5, 0x0080 }, /* R1989 - SLIMTX1MIX Input 3 Volume */
+ { 0x000007C6, 0x0000 }, /* R1990 - SLIMTX1MIX Input 4 Source */
+ { 0x000007C7, 0x0080 }, /* R1991 - SLIMTX1MIX Input 4 Volume */
+ { 0x000007C8, 0x0000 }, /* R1992 - SLIMTX2MIX Input 1 Source */
+ { 0x000007C9, 0x0080 }, /* R1993 - SLIMTX2MIX Input 1 Volume */
+ { 0x000007CA, 0x0000 }, /* R1994 - SLIMTX2MIX Input 2 Source */
+ { 0x000007CB, 0x0080 }, /* R1995 - SLIMTX2MIX Input 2 Volume */
+ { 0x000007CC, 0x0000 }, /* R1996 - SLIMTX2MIX Input 3 Source */
+ { 0x000007CD, 0x0080 }, /* R1997 - SLIMTX2MIX Input 3 Volume */
+ { 0x000007CE, 0x0000 }, /* R1998 - SLIMTX2MIX Input 4 Source */
+ { 0x000007CF, 0x0080 }, /* R1999 - SLIMTX2MIX Input 4 Volume */
+ { 0x000007D0, 0x0000 }, /* R2000 - SLIMTX3MIX Input 1 Source */
+ { 0x000007D1, 0x0080 }, /* R2001 - SLIMTX3MIX Input 1 Volume */
+ { 0x000007D2, 0x0000 }, /* R2002 - SLIMTX3MIX Input 2 Source */
+ { 0x000007D3, 0x0080 }, /* R2003 - SLIMTX3MIX Input 2 Volume */
+ { 0x000007D4, 0x0000 }, /* R2004 - SLIMTX3MIX Input 3 Source */
+ { 0x000007D5, 0x0080 }, /* R2005 - SLIMTX3MIX Input 3 Volume */
+ { 0x000007D6, 0x0000 }, /* R2006 - SLIMTX3MIX Input 4 Source */
+ { 0x000007D7, 0x0080 }, /* R2007 - SLIMTX3MIX Input 4 Volume */
+ { 0x000007D8, 0x0000 }, /* R2008 - SLIMTX4MIX Input 1 Source */
+ { 0x000007D9, 0x0080 }, /* R2009 - SLIMTX4MIX Input 1 Volume */
+ { 0x000007DA, 0x0000 }, /* R2010 - SLIMTX4MIX Input 2 Source */
+ { 0x000007DB, 0x0080 }, /* R2011 - SLIMTX4MIX Input 2 Volume */
+ { 0x000007DC, 0x0000 }, /* R2012 - SLIMTX4MIX Input 3 Source */
+ { 0x000007DD, 0x0080 }, /* R2013 - SLIMTX4MIX Input 3 Volume */
+ { 0x000007DE, 0x0000 }, /* R2014 - SLIMTX4MIX Input 4 Source */
+ { 0x000007DF, 0x0080 }, /* R2015 - SLIMTX4MIX Input 4 Volume */
+ { 0x000007E0, 0x0000 }, /* R2016 - SLIMTX5MIX Input 1 Source */
+ { 0x000007E1, 0x0080 }, /* R2017 - SLIMTX5MIX Input 1 Volume */
+ { 0x000007E2, 0x0000 }, /* R2018 - SLIMTX5MIX Input 2 Source */
+ { 0x000007E3, 0x0080 }, /* R2019 - SLIMTX5MIX Input 2 Volume */
+ { 0x000007E4, 0x0000 }, /* R2020 - SLIMTX5MIX Input 3 Source */
+ { 0x000007E5, 0x0080 }, /* R2021 - SLIMTX5MIX Input 3 Volume */
+ { 0x000007E6, 0x0000 }, /* R2022 - SLIMTX5MIX Input 4 Source */
+ { 0x000007E7, 0x0080 }, /* R2023 - SLIMTX5MIX Input 4 Volume */
+ { 0x000007E8, 0x0000 }, /* R2024 - SLIMTX6MIX Input 1 Source */
+ { 0x000007E9, 0x0080 }, /* R2025 - SLIMTX6MIX Input 1 Volume */
+ { 0x000007EA, 0x0000 }, /* R2026 - SLIMTX6MIX Input 2 Source */
+ { 0x000007EB, 0x0080 }, /* R2027 - SLIMTX6MIX Input 2 Volume */
+ { 0x000007EC, 0x0000 }, /* R2028 - SLIMTX6MIX Input 3 Source */
+ { 0x000007ED, 0x0080 }, /* R2029 - SLIMTX6MIX Input 3 Volume */
+ { 0x000007EE, 0x0000 }, /* R2030 - SLIMTX6MIX Input 4 Source */
+ { 0x000007EF, 0x0080 }, /* R2031 - SLIMTX6MIX Input 4 Volume */
+ { 0x000007F0, 0x0000 }, /* R2032 - SLIMTX7MIX Input 1 Source */
+ { 0x000007F1, 0x0080 }, /* R2033 - SLIMTX7MIX Input 1 Volume */
+ { 0x000007F2, 0x0000 }, /* R2034 - SLIMTX7MIX Input 2 Source */
+ { 0x000007F3, 0x0080 }, /* R2035 - SLIMTX7MIX Input 2 Volume */
+ { 0x000007F4, 0x0000 }, /* R2036 - SLIMTX7MIX Input 3 Source */
+ { 0x000007F5, 0x0080 }, /* R2037 - SLIMTX7MIX Input 3 Volume */
+ { 0x000007F6, 0x0000 }, /* R2038 - SLIMTX7MIX Input 4 Source */
+ { 0x000007F7, 0x0080 }, /* R2039 - SLIMTX7MIX Input 4 Volume */
+ { 0x000007F8, 0x0000 }, /* R2040 - SLIMTX8MIX Input 1 Source */
+ { 0x000007F9, 0x0080 }, /* R2041 - SLIMTX8MIX Input 1 Volume */
+ { 0x000007FA, 0x0000 }, /* R2042 - SLIMTX8MIX Input 2 Source */
+ { 0x000007FB, 0x0080 }, /* R2043 - SLIMTX8MIX Input 2 Volume */
+ { 0x000007FC, 0x0000 }, /* R2044 - SLIMTX8MIX Input 3 Source */
+ { 0x000007FD, 0x0080 }, /* R2045 - SLIMTX8MIX Input 3 Volume */
+ { 0x000007FE, 0x0000 }, /* R2046 - SLIMTX8MIX Input 4 Source */
+ { 0x000007FF, 0x0080 }, /* R2047 - SLIMTX8MIX Input 4 Volume */
+ { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */
+ { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */
+ { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */
+ { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */
+ { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */
+ { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */
+ { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */
+ { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */
+ { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */
+ { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */
+ { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */
+ { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */
+ { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */
+ { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */
+ { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */
+ { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */
+ { 0x00000890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */
+ { 0x00000891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */
+ { 0x00000892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */
+ { 0x00000893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */
+ { 0x00000894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */
+ { 0x00000895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */
+ { 0x00000896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */
+ { 0x00000897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */
+ { 0x00000898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */
+ { 0x00000899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */
+ { 0x0000089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */
+ { 0x0000089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */
+ { 0x0000089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */
+ { 0x0000089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */
+ { 0x0000089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */
+ { 0x0000089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */
+ { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */
+ { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */
+ { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */
+ { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */
+ { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */
+ { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */
+ { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */
+ { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */
+ { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */
+ { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */
+ { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */
+ { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */
+ { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */
+ { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
+ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
+ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
+ { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
+ { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
+ { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
+ { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
+ { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
+ { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
+ { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
+ { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
+ { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
+ { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
+ { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
+ { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
+ { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
+ { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
+ { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
+ { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
+ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
+ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
+ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
+ { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */
+ { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */
+ { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */
+ { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */
+ { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */
+ { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */
+ { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */
+ { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */
+ { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */
+ { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */
+ { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */
+ { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */
+ { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */
+ { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */
+ { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */
+ { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */
+ { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */
+ { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */
+ { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */
+ { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */
+ { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */
+ { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */
+ { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */
+ { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */
+ { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */
+ { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */
+ { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */
+ { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */
+ { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */
+ { 0x00000940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */
+ { 0x00000941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */
+ { 0x00000942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */
+ { 0x00000943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */
+ { 0x00000944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */
+ { 0x00000945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */
+ { 0x00000946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */
+ { 0x00000947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */
+ { 0x00000948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */
+ { 0x00000949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */
+ { 0x0000094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */
+ { 0x0000094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */
+ { 0x0000094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */
+ { 0x0000094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */
+ { 0x0000094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */
+ { 0x0000094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */
+ { 0x00000950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */
+ { 0x00000958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */
+ { 0x00000960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */
+ { 0x00000968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */
+ { 0x00000970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */
+ { 0x00000978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */
+ { 0x00000A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */
+ { 0x00000A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */
+ { 0x00000A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */
+ { 0x00000A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */
+ { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */
+ { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */
+ { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */
+ { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */
+ { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */
+ { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */
+ { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */
+ { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */
+ { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */
+ { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */
+ { 0x00000C02, 0xA101 }, /* R3074 - GPIO3 CTRL */
+ { 0x00000C03, 0xA101 }, /* R3075 - GPIO4 CTRL */
+ { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */
+ { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */
+ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */
+ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */
+ { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */
+ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */
+ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */
+ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */
+ { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */
+ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */
+ { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */
+ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */
+ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */
+ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */
+ { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */
+ { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */
+ { 0x00000D19, 0xFFFF }, /* R3353 - IRQ2 Status 2 Mask */
+ { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */
+ { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
+ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
+ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
+ { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */
+ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
+ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
+ { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
+ { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */
+ { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */
+ { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */
+ { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */
+ { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */
+ { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */
+ { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */
+ { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */
+ { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */
+ { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */
+ { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */
+ { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */
+ { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */
+ { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */
+ { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */
+ { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */
+ { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */
+ { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */
+ { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */
+ { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */
+ { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */
+ { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */
+ { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */
+ { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */
+ { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */
+ { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */
+ { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */
+ { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */
+ { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */
+ { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */
+ { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */
+ { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */
+ { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */
+ { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */
+ { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */
+ { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */
+ { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */
+ { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */
+ { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */
+ { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */
+ { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */
+ { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */
+ { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */
+ { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */
+ { 0x00000E3C, 0x6318 }, /* R3644 - EQ3_1 */
+ { 0x00000E3D, 0x6300 }, /* R3645 - EQ3_2 */
+ { 0x00000E3E, 0x0FC8 }, /* R3646 - EQ3_3 */
+ { 0x00000E3F, 0x03FE }, /* R3647 - EQ3_4 */
+ { 0x00000E40, 0x00E0 }, /* R3648 - EQ3_5 */
+ { 0x00000E41, 0x1EC4 }, /* R3649 - EQ3_6 */
+ { 0x00000E42, 0xF136 }, /* R3650 - EQ3_7 */
+ { 0x00000E43, 0x0409 }, /* R3651 - EQ3_8 */
+ { 0x00000E44, 0x04CC }, /* R3652 - EQ3_9 */
+ { 0x00000E45, 0x1C9B }, /* R3653 - EQ3_10 */
+ { 0x00000E46, 0xF337 }, /* R3654 - EQ3_11 */
+ { 0x00000E47, 0x040B }, /* R3655 - EQ3_12 */
+ { 0x00000E48, 0x0CBB }, /* R3656 - EQ3_13 */
+ { 0x00000E49, 0x16F8 }, /* R3657 - EQ3_14 */
+ { 0x00000E4A, 0xF7D9 }, /* R3658 - EQ3_15 */
+ { 0x00000E4B, 0x040A }, /* R3659 - EQ3_16 */
+ { 0x00000E4C, 0x1F14 }, /* R3660 - EQ3_17 */
+ { 0x00000E4D, 0x058C }, /* R3661 - EQ3_18 */
+ { 0x00000E4E, 0x0563 }, /* R3662 - EQ3_19 */
+ { 0x00000E4F, 0x4000 }, /* R3663 - EQ3_20 */
+ { 0x00000E50, 0x0B75 }, /* R3664 - EQ3_21 */
+ { 0x00000E52, 0x6318 }, /* R3666 - EQ4_1 */
+ { 0x00000E53, 0x6300 }, /* R3667 - EQ4_2 */
+ { 0x00000E54, 0x0FC8 }, /* R3668 - EQ4_3 */
+ { 0x00000E55, 0x03FE }, /* R3669 - EQ4_4 */
+ { 0x00000E56, 0x00E0 }, /* R3670 - EQ4_5 */
+ { 0x00000E57, 0x1EC4 }, /* R3671 - EQ4_6 */
+ { 0x00000E58, 0xF136 }, /* R3672 - EQ4_7 */
+ { 0x00000E59, 0x0409 }, /* R3673 - EQ4_8 */
+ { 0x00000E5A, 0x04CC }, /* R3674 - EQ4_9 */
+ { 0x00000E5B, 0x1C9B }, /* R3675 - EQ4_10 */
+ { 0x00000E5C, 0xF337 }, /* R3676 - EQ4_11 */
+ { 0x00000E5D, 0x040B }, /* R3677 - EQ4_12 */
+ { 0x00000E5E, 0x0CBB }, /* R3678 - EQ4_13 */
+ { 0x00000E5F, 0x16F8 }, /* R3679 - EQ4_14 */
+ { 0x00000E60, 0xF7D9 }, /* R3680 - EQ4_15 */
+ { 0x00000E61, 0x040A }, /* R3681 - EQ4_16 */
+ { 0x00000E62, 0x1F14 }, /* R3682 - EQ4_17 */
+ { 0x00000E63, 0x058C }, /* R3683 - EQ4_18 */
+ { 0x00000E64, 0x0563 }, /* R3684 - EQ4_19 */
+ { 0x00000E65, 0x4000 }, /* R3685 - EQ4_20 */
+ { 0x00000E66, 0x0B75 }, /* R3686 - EQ4_21 */
+ { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */
+ { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */
+ { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
+ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
+ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
+ { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
+ { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
+ { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
+ { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
+ { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
+ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
+ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
+ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
+ { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */
+ { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */
+ { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
+ { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
+ { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
+ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
+ { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */
+ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
+ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
+ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
+ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
+ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
+ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
+ { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
+ { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
+ { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
+ { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
+ { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
+};
+
+static bool wm5102_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_CTRL_IF_SPI_CFG_1:
+ case ARIZONA_CTRL_IF_I2C1_CFG_1:
+ case ARIZONA_CTRL_IF_STATUS_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+ case ARIZONA_WRITE_SEQUENCER_PROM:
+ case ARIZONA_TONE_GENERATOR_1:
+ case ARIZONA_TONE_GENERATOR_2:
+ case ARIZONA_TONE_GENERATOR_3:
+ case ARIZONA_TONE_GENERATOR_4:
+ case ARIZONA_TONE_GENERATOR_5:
+ case ARIZONA_PWM_DRIVE_1:
+ case ARIZONA_PWM_DRIVE_2:
+ case ARIZONA_PWM_DRIVE_3:
+ case ARIZONA_WAKE_CONTROL:
+ case ARIZONA_SEQUENCE_CONTROL:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6:
+ case ARIZONA_COMFORT_NOISE_GENERATOR:
+ case ARIZONA_HAPTICS_CONTROL_1:
+ case ARIZONA_HAPTICS_CONTROL_2:
+ case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_1_DURATION:
+ case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_2_DURATION:
+ case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_3_DURATION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_CLOCK_32K_1:
+ case ARIZONA_SYSTEM_CLOCK_1:
+ case ARIZONA_SAMPLE_RATE_1:
+ case ARIZONA_SAMPLE_RATE_2:
+ case ARIZONA_SAMPLE_RATE_3:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_CLOCK_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+ case ARIZONA_OUTPUT_ASYNC_CLOCK:
+ case ARIZONA_RATE_ESTIMATOR_1:
+ case ARIZONA_RATE_ESTIMATOR_2:
+ case ARIZONA_RATE_ESTIMATOR_3:
+ case ARIZONA_RATE_ESTIMATOR_4:
+ case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_FLL1_CONTROL_1:
+ case ARIZONA_FLL1_CONTROL_2:
+ case ARIZONA_FLL1_CONTROL_3:
+ case ARIZONA_FLL1_CONTROL_4:
+ case ARIZONA_FLL1_CONTROL_5:
+ case ARIZONA_FLL1_CONTROL_6:
+ case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL1_SYNCHRONISER_1:
+ case ARIZONA_FLL1_SYNCHRONISER_2:
+ case ARIZONA_FLL1_SYNCHRONISER_3:
+ case ARIZONA_FLL1_SYNCHRONISER_4:
+ case ARIZONA_FLL1_SYNCHRONISER_5:
+ case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SPREAD_SPECTRUM:
+ case ARIZONA_FLL1_GPIO_CLOCK:
+ case ARIZONA_FLL2_CONTROL_1:
+ case ARIZONA_FLL2_CONTROL_2:
+ case ARIZONA_FLL2_CONTROL_3:
+ case ARIZONA_FLL2_CONTROL_4:
+ case ARIZONA_FLL2_CONTROL_5:
+ case ARIZONA_FLL2_CONTROL_6:
+ case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL2_SYNCHRONISER_1:
+ case ARIZONA_FLL2_SYNCHRONISER_2:
+ case ARIZONA_FLL2_SYNCHRONISER_3:
+ case ARIZONA_FLL2_SYNCHRONISER_4:
+ case ARIZONA_FLL2_SYNCHRONISER_5:
+ case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SPREAD_SPECTRUM:
+ case ARIZONA_FLL2_GPIO_CLOCK:
+ case ARIZONA_MIC_CHARGE_PUMP_1:
+ case ARIZONA_LDO1_CONTROL_1:
+ case ARIZONA_LDO2_CONTROL_1:
+ case ARIZONA_MIC_BIAS_CTRL_1:
+ case ARIZONA_MIC_BIAS_CTRL_2:
+ case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_ACCESSORY_DETECT_MODE_1:
+ case ARIZONA_HEADPHONE_DETECT_1:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_MIC_DETECT_1:
+ case ARIZONA_MIC_DETECT_2:
+ case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+ case ARIZONA_ISOLATION_CONTROL:
+ case ARIZONA_JACK_DETECT_ANALOGUE:
+ case ARIZONA_INPUT_ENABLES:
+ case ARIZONA_INPUT_RATE:
+ case ARIZONA_INPUT_VOLUME_RAMP:
+ case ARIZONA_IN1L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DMIC1L_CONTROL:
+ case ARIZONA_IN1R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DMIC1R_CONTROL:
+ case ARIZONA_IN2L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DMIC2L_CONTROL:
+ case ARIZONA_IN2R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DMIC2R_CONTROL:
+ case ARIZONA_IN3L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_3L:
+ case ARIZONA_DMIC3L_CONTROL:
+ case ARIZONA_IN3R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_3R:
+ case ARIZONA_DMIC3R_CONTROL:
+ case ARIZONA_OUTPUT_ENABLES_1:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_OUTPUT_RATE_1:
+ case ARIZONA_OUTPUT_VOLUME_RAMP:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DAC_VOLUME_LIMIT_1L:
+ case ARIZONA_NOISE_GATE_SELECT_1L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DAC_VOLUME_LIMIT_1R:
+ case ARIZONA_NOISE_GATE_SELECT_1R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_2L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DAC_VOLUME_LIMIT_2L:
+ case ARIZONA_NOISE_GATE_SELECT_2L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_2R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DAC_VOLUME_LIMIT_2R:
+ case ARIZONA_NOISE_GATE_SELECT_2R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_3L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_3L:
+ case ARIZONA_DAC_VOLUME_LIMIT_3L:
+ case ARIZONA_NOISE_GATE_SELECT_3L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_3R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_3R:
+ case ARIZONA_DAC_VOLUME_LIMIT_3R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_4L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+ case ARIZONA_OUT_VOLUME_4L:
+ case ARIZONA_NOISE_GATE_SELECT_4L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_4R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4R:
+ case ARIZONA_OUT_VOLUME_4R:
+ case ARIZONA_NOISE_GATE_SELECT_4R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_5L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5L:
+ case ARIZONA_DAC_VOLUME_LIMIT_5L:
+ case ARIZONA_NOISE_GATE_SELECT_5L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_5R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5R:
+ case ARIZONA_DAC_VOLUME_LIMIT_5R:
+ case ARIZONA_NOISE_GATE_SELECT_5R:
+ case ARIZONA_DAC_AEC_CONTROL_1:
+ case ARIZONA_NOISE_GATE_CONTROL:
+ case ARIZONA_PDM_SPK1_CTRL_1:
+ case ARIZONA_PDM_SPK1_CTRL_2:
+ case ARIZONA_DAC_COMP_1:
+ case ARIZONA_DAC_COMP_2:
+ case ARIZONA_DAC_COMP_3:
+ case ARIZONA_DAC_COMP_4:
+ case ARIZONA_AIF1_BCLK_CTRL:
+ case ARIZONA_AIF1_TX_PIN_CTRL:
+ case ARIZONA_AIF1_RX_PIN_CTRL:
+ case ARIZONA_AIF1_RATE_CTRL:
+ case ARIZONA_AIF1_FORMAT:
+ case ARIZONA_AIF1_TX_BCLK_RATE:
+ case ARIZONA_AIF1_RX_BCLK_RATE:
+ case ARIZONA_AIF1_FRAME_CTRL_1:
+ case ARIZONA_AIF1_FRAME_CTRL_2:
+ case ARIZONA_AIF1_FRAME_CTRL_3:
+ case ARIZONA_AIF1_FRAME_CTRL_4:
+ case ARIZONA_AIF1_FRAME_CTRL_5:
+ case ARIZONA_AIF1_FRAME_CTRL_6:
+ case ARIZONA_AIF1_FRAME_CTRL_7:
+ case ARIZONA_AIF1_FRAME_CTRL_8:
+ case ARIZONA_AIF1_FRAME_CTRL_9:
+ case ARIZONA_AIF1_FRAME_CTRL_10:
+ case ARIZONA_AIF1_FRAME_CTRL_11:
+ case ARIZONA_AIF1_FRAME_CTRL_12:
+ case ARIZONA_AIF1_FRAME_CTRL_13:
+ case ARIZONA_AIF1_FRAME_CTRL_14:
+ case ARIZONA_AIF1_FRAME_CTRL_15:
+ case ARIZONA_AIF1_FRAME_CTRL_16:
+ case ARIZONA_AIF1_FRAME_CTRL_17:
+ case ARIZONA_AIF1_FRAME_CTRL_18:
+ case ARIZONA_AIF1_TX_ENABLES:
+ case ARIZONA_AIF1_RX_ENABLES:
+ case ARIZONA_AIF1_FORCE_WRITE:
+ case ARIZONA_AIF2_BCLK_CTRL:
+ case ARIZONA_AIF2_TX_PIN_CTRL:
+ case ARIZONA_AIF2_RX_PIN_CTRL:
+ case ARIZONA_AIF2_RATE_CTRL:
+ case ARIZONA_AIF2_FORMAT:
+ case ARIZONA_AIF2_TX_BCLK_RATE:
+ case ARIZONA_AIF2_RX_BCLK_RATE:
+ case ARIZONA_AIF2_FRAME_CTRL_1:
+ case ARIZONA_AIF2_FRAME_CTRL_2:
+ case ARIZONA_AIF2_FRAME_CTRL_3:
+ case ARIZONA_AIF2_FRAME_CTRL_4:
+ case ARIZONA_AIF2_FRAME_CTRL_11:
+ case ARIZONA_AIF2_FRAME_CTRL_12:
+ case ARIZONA_AIF2_TX_ENABLES:
+ case ARIZONA_AIF2_RX_ENABLES:
+ case ARIZONA_AIF2_FORCE_WRITE:
+ case ARIZONA_AIF3_BCLK_CTRL:
+ case ARIZONA_AIF3_TX_PIN_CTRL:
+ case ARIZONA_AIF3_RX_PIN_CTRL:
+ case ARIZONA_AIF3_RATE_CTRL:
+ case ARIZONA_AIF3_FORMAT:
+ case ARIZONA_AIF3_TX_BCLK_RATE:
+ case ARIZONA_AIF3_RX_BCLK_RATE:
+ case ARIZONA_AIF3_FRAME_CTRL_1:
+ case ARIZONA_AIF3_FRAME_CTRL_2:
+ case ARIZONA_AIF3_FRAME_CTRL_3:
+ case ARIZONA_AIF3_FRAME_CTRL_4:
+ case ARIZONA_AIF3_FRAME_CTRL_11:
+ case ARIZONA_AIF3_FRAME_CTRL_12:
+ case ARIZONA_AIF3_TX_ENABLES:
+ case ARIZONA_AIF3_RX_ENABLES:
+ case ARIZONA_AIF3_FORCE_WRITE:
+ case ARIZONA_SLIMBUS_FRAMER_REF_GEAR:
+ case ARIZONA_SLIMBUS_RATES_1:
+ case ARIZONA_SLIMBUS_RATES_2:
+ case ARIZONA_SLIMBUS_RATES_3:
+ case ARIZONA_SLIMBUS_RATES_4:
+ case ARIZONA_SLIMBUS_RATES_5:
+ case ARIZONA_SLIMBUS_RATES_6:
+ case ARIZONA_SLIMBUS_RATES_7:
+ case ARIZONA_SLIMBUS_RATES_8:
+ case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+ case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+ case ARIZONA_MICMIX_INPUT_1_SOURCE:
+ case ARIZONA_MICMIX_INPUT_1_VOLUME:
+ case ARIZONA_MICMIX_INPUT_2_SOURCE:
+ case ARIZONA_MICMIX_INPUT_2_VOLUME:
+ case ARIZONA_MICMIX_INPUT_3_SOURCE:
+ case ARIZONA_MICMIX_INPUT_3_VOLUME:
+ case ARIZONA_MICMIX_INPUT_4_SOURCE:
+ case ARIZONA_MICMIX_INPUT_4_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_1_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_1_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_2_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_2_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_3_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_3_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_4_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_GPIO1_CTRL:
+ case ARIZONA_GPIO2_CTRL:
+ case ARIZONA_GPIO3_CTRL:
+ case ARIZONA_GPIO4_CTRL:
+ case ARIZONA_GPIO5_CTRL:
+ case ARIZONA_IRQ_CTRL_1:
+ case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+ case ARIZONA_MISC_PAD_CTRL_1:
+ case ARIZONA_MISC_PAD_CTRL_2:
+ case ARIZONA_MISC_PAD_CTRL_3:
+ case ARIZONA_MISC_PAD_CTRL_4:
+ case ARIZONA_MISC_PAD_CTRL_5:
+ case ARIZONA_MISC_PAD_CTRL_6:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_INTERRUPT_STATUS_1_MASK:
+ case ARIZONA_INTERRUPT_STATUS_2_MASK:
+ case ARIZONA_INTERRUPT_STATUS_3_MASK:
+ case ARIZONA_INTERRUPT_STATUS_4_MASK:
+ case ARIZONA_INTERRUPT_STATUS_5_MASK:
+ case ARIZONA_INTERRUPT_CONTROL:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1_MASK:
+ case ARIZONA_IRQ2_STATUS_2_MASK:
+ case ARIZONA_IRQ2_STATUS_3_MASK:
+ case ARIZONA_IRQ2_STATUS_4_MASK:
+ case ARIZONA_IRQ2_STATUS_5_MASK:
+ case ARIZONA_IRQ2_CONTROL:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_ADSP2_IRQ0:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_MASK_IRQ1:
+ case ARIZONA_AOD_IRQ_MASK_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_JACK_DETECT_DEBOUNCE:
+ case ARIZONA_FX_CTRL1:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_EQ1_1:
+ case ARIZONA_EQ1_2:
+ case ARIZONA_EQ1_3:
+ case ARIZONA_EQ1_4:
+ case ARIZONA_EQ1_5:
+ case ARIZONA_EQ1_6:
+ case ARIZONA_EQ1_7:
+ case ARIZONA_EQ1_8:
+ case ARIZONA_EQ1_9:
+ case ARIZONA_EQ1_10:
+ case ARIZONA_EQ1_11:
+ case ARIZONA_EQ1_12:
+ case ARIZONA_EQ1_13:
+ case ARIZONA_EQ1_14:
+ case ARIZONA_EQ1_15:
+ case ARIZONA_EQ1_16:
+ case ARIZONA_EQ1_17:
+ case ARIZONA_EQ1_18:
+ case ARIZONA_EQ1_19:
+ case ARIZONA_EQ1_20:
+ case ARIZONA_EQ1_21:
+ case ARIZONA_EQ2_1:
+ case ARIZONA_EQ2_2:
+ case ARIZONA_EQ2_3:
+ case ARIZONA_EQ2_4:
+ case ARIZONA_EQ2_5:
+ case ARIZONA_EQ2_6:
+ case ARIZONA_EQ2_7:
+ case ARIZONA_EQ2_8:
+ case ARIZONA_EQ2_9:
+ case ARIZONA_EQ2_10:
+ case ARIZONA_EQ2_11:
+ case ARIZONA_EQ2_12:
+ case ARIZONA_EQ2_13:
+ case ARIZONA_EQ2_14:
+ case ARIZONA_EQ2_15:
+ case ARIZONA_EQ2_16:
+ case ARIZONA_EQ2_17:
+ case ARIZONA_EQ2_18:
+ case ARIZONA_EQ2_19:
+ case ARIZONA_EQ2_20:
+ case ARIZONA_EQ2_21:
+ case ARIZONA_EQ3_1:
+ case ARIZONA_EQ3_2:
+ case ARIZONA_EQ3_3:
+ case ARIZONA_EQ3_4:
+ case ARIZONA_EQ3_5:
+ case ARIZONA_EQ3_6:
+ case ARIZONA_EQ3_7:
+ case ARIZONA_EQ3_8:
+ case ARIZONA_EQ3_9:
+ case ARIZONA_EQ3_10:
+ case ARIZONA_EQ3_11:
+ case ARIZONA_EQ3_12:
+ case ARIZONA_EQ3_13:
+ case ARIZONA_EQ3_14:
+ case ARIZONA_EQ3_15:
+ case ARIZONA_EQ3_16:
+ case ARIZONA_EQ3_17:
+ case ARIZONA_EQ3_18:
+ case ARIZONA_EQ3_19:
+ case ARIZONA_EQ3_20:
+ case ARIZONA_EQ3_21:
+ case ARIZONA_EQ4_1:
+ case ARIZONA_EQ4_2:
+ case ARIZONA_EQ4_3:
+ case ARIZONA_EQ4_4:
+ case ARIZONA_EQ4_5:
+ case ARIZONA_EQ4_6:
+ case ARIZONA_EQ4_7:
+ case ARIZONA_EQ4_8:
+ case ARIZONA_EQ4_9:
+ case ARIZONA_EQ4_10:
+ case ARIZONA_EQ4_11:
+ case ARIZONA_EQ4_12:
+ case ARIZONA_EQ4_13:
+ case ARIZONA_EQ4_14:
+ case ARIZONA_EQ4_15:
+ case ARIZONA_EQ4_16:
+ case ARIZONA_EQ4_17:
+ case ARIZONA_EQ4_18:
+ case ARIZONA_EQ4_19:
+ case ARIZONA_EQ4_20:
+ case ARIZONA_EQ4_21:
+ case ARIZONA_DRC1_CTRL1:
+ case ARIZONA_DRC1_CTRL2:
+ case ARIZONA_DRC1_CTRL3:
+ case ARIZONA_DRC1_CTRL4:
+ case ARIZONA_DRC1_CTRL5:
+ case ARIZONA_DRC2_CTRL1:
+ case ARIZONA_DRC2_CTRL2:
+ case ARIZONA_DRC2_CTRL3:
+ case ARIZONA_DRC2_CTRL4:
+ case ARIZONA_DRC2_CTRL5:
+ case ARIZONA_HPLPF1_1:
+ case ARIZONA_HPLPF1_2:
+ case ARIZONA_HPLPF2_1:
+ case ARIZONA_HPLPF2_2:
+ case ARIZONA_HPLPF3_1:
+ case ARIZONA_HPLPF3_2:
+ case ARIZONA_HPLPF4_1:
+ case ARIZONA_HPLPF4_2:
+ case ARIZONA_ASRC_ENABLE:
+ case ARIZONA_ASRC_RATE1:
+ case ARIZONA_ASRC_RATE2:
+ case ARIZONA_ISRC_1_CTRL_1:
+ case ARIZONA_ISRC_1_CTRL_2:
+ case ARIZONA_ISRC_1_CTRL_3:
+ case ARIZONA_ISRC_2_CTRL_1:
+ case ARIZONA_ISRC_2_CTRL_2:
+ case ARIZONA_ISRC_2_CTRL_3:
+ case ARIZONA_ISRC_3_CTRL_1:
+ case ARIZONA_ISRC_3_CTRL_2:
+ case ARIZONA_ISRC_3_CTRL_3:
+ case ARIZONA_DSP1_CONTROL_1:
+ case ARIZONA_DSP1_CLOCKING_1:
+ case ARIZONA_DSP1_STATUS_1:
+ case ARIZONA_DSP1_STATUS_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_DSP1_STATUS_1:
+ case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_MIC_DETECT_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config wm5102_spi_regmap = {
+ .reg_bits = 32,
+ .pad_bits = 16,
+ .val_bits = 16,
+
+ .max_register = ARIZONA_DSP1_STATUS_2,
+ .readable_reg = wm5102_readable_register,
+ .volatile_reg = wm5102_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm5102_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5102_spi_regmap);
+
+const struct regmap_config wm5102_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+
+ .max_register = ARIZONA_DSP1_STATUS_2,
+ .readable_reg = wm5102_readable_register,
+ .volatile_reg = wm5102_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm5102_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5102_i2c_regmap);
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
new file mode 100644
index 000000000000..bd8782c8896b
--- /dev/null
+++ b/drivers/mfd/wm5110-tables.c
@@ -0,0 +1,2281 @@
+/*
+ * wm5110-tables.c -- WM5110 data tables
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define WM5110_NUM_AOD_ISR 2
+#define WM5110_NUM_ISR 5
+
+static const struct reg_default wm5110_reva_patch[] = {
+ { 0x80, 0x3 },
+ { 0x44, 0x20 },
+ { 0x45, 0x40 },
+ { 0x46, 0x60 },
+ { 0x47, 0x80 },
+ { 0x48, 0xa0 },
+ { 0x51, 0x13 },
+ { 0x52, 0x33 },
+ { 0x53, 0x53 },
+ { 0x54, 0x73 },
+ { 0x55, 0x75 },
+ { 0x56, 0xb3 },
+ { 0x2ef, 0x124 },
+ { 0x2ef, 0x124 },
+ { 0x2f0, 0x124 },
+ { 0x2f0, 0x124 },
+ { 0x2f1, 0x124 },
+ { 0x2f1, 0x124 },
+ { 0x2f2, 0x124 },
+ { 0x2f2, 0x124 },
+ { 0x2f3, 0x124 },
+ { 0x2f3, 0x124 },
+ { 0x2f4, 0x124 },
+ { 0x2f4, 0x124 },
+ { 0x2eb, 0x60 },
+ { 0x2ec, 0x60 },
+ { 0x2ed, 0x60 },
+ { 0xc30, 0x3e3e },
+ { 0xc30, 0x3e3e },
+ { 0xc31, 0x3e },
+ { 0xc32, 0x3e3e },
+ { 0xc32, 0x3e3e },
+ { 0xc33, 0x3e3e },
+ { 0xc33, 0x3e3e },
+ { 0xc34, 0x3e3e },
+ { 0xc34, 0x3e3e },
+ { 0xc35, 0x3e3e },
+ { 0xc35, 0x3e3e },
+ { 0xc36, 0x3e3e },
+ { 0xc36, 0x3e3e },
+ { 0xc37, 0x3e3e },
+ { 0xc37, 0x3e3e },
+ { 0xc38, 0x3e3e },
+ { 0xc38, 0x3e3e },
+ { 0xc30, 0x3e3e },
+ { 0xc30, 0x3e3e },
+ { 0xc39, 0x3e3e },
+ { 0xc39, 0x3e3e },
+ { 0xc3a, 0x3e3e },
+ { 0xc3a, 0x3e3e },
+ { 0xc3b, 0x3e3e },
+ { 0xc3b, 0x3e3e },
+ { 0xc3c, 0x3e },
+ { 0x201, 0x18a5 },
+ { 0x201, 0x18a5 },
+ { 0x201, 0x18a5 },
+ { 0x202, 0x4100 },
+ { 0x460, 0xc00 },
+ { 0x461, 0x8000 },
+ { 0x462, 0xc01 },
+ { 0x463, 0x50f0 },
+ { 0x464, 0xc01 },
+ { 0x465, 0x4820 },
+ { 0x466, 0xc01 },
+ { 0x466, 0xc01 },
+ { 0x467, 0x4040 },
+ { 0x468, 0xc01 },
+ { 0x468, 0xc01 },
+ { 0x469, 0x3940 },
+ { 0x46a, 0xc01 },
+ { 0x46a, 0xc01 },
+ { 0x46a, 0xc01 },
+ { 0x46b, 0x3310 },
+ { 0x46c, 0x801 },
+ { 0x46c, 0x801 },
+ { 0x46d, 0x2d80 },
+ { 0x46e, 0x801 },
+ { 0x46e, 0x801 },
+ { 0x46f, 0x2890 },
+ { 0x470, 0x801 },
+ { 0x470, 0x801 },
+ { 0x471, 0x1990 },
+ { 0x472, 0x801 },
+ { 0x472, 0x801 },
+ { 0x473, 0x1450 },
+ { 0x474, 0x801 },
+ { 0x474, 0x801 },
+ { 0x474, 0x801 },
+ { 0x475, 0x1020 },
+ { 0x476, 0x801 },
+ { 0x476, 0x801 },
+ { 0x476, 0x801 },
+ { 0x477, 0xcd0 },
+ { 0x478, 0x806 },
+ { 0x478, 0x806 },
+ { 0x479, 0xa30 },
+ { 0x47a, 0x806 },
+ { 0x47a, 0x806 },
+ { 0x47b, 0x810 },
+ { 0x47c, 0x80e },
+ { 0x47c, 0x80e },
+ { 0x47d, 0x510 },
+ { 0x47e, 0x81f },
+ { 0x47e, 0x81f },
+ { 0x2DB, 0x0A00 },
+ { 0x2DD, 0x0023 },
+ { 0x2DF, 0x0102 },
+ { 0x80, 0x0 },
+ { 0xC20, 0x0002 },
+ { 0x209, 0x002A },
+};
+
+/* We use a function so we can use ARRAY_SIZE() */
+int wm5110_patch(struct arizona *arizona)
+{
+ switch (arizona->rev) {
+ case 0:
+ case 1:
+ return regmap_register_patch(arizona->regmap,
+ wm5110_reva_patch,
+ ARRAY_SIZE(wm5110_reva_patch));
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(wm5110_patch);
+
+static const struct regmap_irq wm5110_aod_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP5_FALL] = { .mask = ARIZONA_GP5_FALL_EINT1 },
+ [ARIZONA_IRQ_GP5_RISE] = { .mask = ARIZONA_GP5_RISE_EINT1 },
+ [ARIZONA_IRQ_JD_FALL] = { .mask = ARIZONA_JD1_FALL_EINT1 },
+ [ARIZONA_IRQ_JD_RISE] = { .mask = ARIZONA_JD1_RISE_EINT1 },
+};
+
+const struct regmap_irq_chip wm5110_aod = {
+ .name = "wm5110 AOD",
+ .status_base = ARIZONA_AOD_IRQ1,
+ .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1,
+ .ack_base = ARIZONA_AOD_IRQ1,
+ .wake_base = ARIZONA_WAKE_CONTROL,
+ .num_regs = 1,
+ .irqs = wm5110_aod_irqs,
+ .num_irqs = ARRAY_SIZE(wm5110_aod_irqs),
+};
+EXPORT_SYMBOL_GPL(wm5110_aod);
+
+static const struct regmap_irq wm5110_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 },
+ [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 },
+ [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+ [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+ [ARIZONA_IRQ_DSP4_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP4_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP3_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP3_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP2_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP2_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP1_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ8] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ8_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ7] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ7_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ6] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ6_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ5] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ5_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ4] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ4_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ3] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ3_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ2] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ1] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1
+ },
+
+ [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1
+ },
+ [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+ },
+ [ARIZONA_IRQ_HPDET] = {
+ .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1
+ },
+ [ARIZONA_IRQ_MICDET] = {
+ .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1
+ },
+ [ARIZONA_IRQ_WSEQ_DONE] = {
+ .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DRC2_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_DRC1_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_ASRC2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_ASRC1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_UNDERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_OVERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+ },
+
+ [ARIZONA_IRQ_ASRC_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ASRC_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF3_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF3_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF2_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF2_ERR_EINT1
+ },
+ [ARIZONA_IRQ_AIF1_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_AIF1_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CTRLIF_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_CTRLIF_ERR_EINT1
+ },
+ [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+ .reg_offset = 3, .mask = ARIZONA_MIXER_DROPPED_SAMPLE_EINT1
+ },
+ [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_ASYNC_CLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_SYSCLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC1_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1
+ },
+
+ [ARIZONA_IRQ_BOOT_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_DAC_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_DAC_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DCS_HP_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_DCS_HP_DONE_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+ },
+};
+
+const struct regmap_irq_chip wm5110_irq = {
+ .name = "wm5110 IRQ",
+ .status_base = ARIZONA_INTERRUPT_STATUS_1,
+ .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+ .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+ .num_regs = 5,
+ .irqs = wm5110_irqs,
+ .num_irqs = ARRAY_SIZE(wm5110_irqs),
+};
+EXPORT_SYMBOL_GPL(wm5110_irq);
+
+static const struct reg_default wm5110_reg_default[] = {
+ { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
+ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
+ { 0x0000000A, 0x0001 }, /* R10 - Ctrl IF I2C2 CFG 1 */
+ { 0x0000000B, 0x0036 }, /* R11 - Ctrl IF I2C1 CFG 2 */
+ { 0x0000000C, 0x0036 }, /* R12 - Ctrl IF I2C2 CFG 2 */
+ { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
+ { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
+ { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
+ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
+ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
+ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
+ { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */
+ { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */
+ { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */
+ { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */
+ { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */
+ { 0x00000040, 0x0000 }, /* R64 - Wake control */
+ { 0x00000041, 0x0000 }, /* R65 - Sequence control */
+ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */
+ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
+ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
+ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
+ { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
+ { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
+ { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
+ { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
+ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
+ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
+ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
+ { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */
+ { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */
+ { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */
+ { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
+ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
+ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
+ { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */
+ { 0x00000101, 0x0504 }, /* R257 - System Clock 1 */
+ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
+ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
+ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
+ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
+ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000149, 0x0000 }, /* R329 - Output system clock */
+ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */
+ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
+ { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */
+ { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
+ { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
+ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
+ { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
+ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
+ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
+ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
+ { 0x00000175, 0x0006 }, /* R373 - FLL1 Control 5 */
+ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
+ { 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
+ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
+ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
+ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
+ { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
+ { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
+ { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
+ { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
+ { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
+ { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */
+ { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */
+ { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */
+ { 0x00000195, 0x000C }, /* R405 - FLL2 Control 5 */
+ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
+ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
+ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
+ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
+ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
+ { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
+ { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
+ { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
+ { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
+ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
+ { 0x00000210, 0x0184 }, /* R528 - LDO1 Control 1 */
+ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */
+ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
+ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
+ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
+ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
+ { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
+ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
+ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
+ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
+ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
+ { 0x00000300, 0x0000 }, /* R768 - Input Enables */
+ { 0x00000308, 0x0000 }, /* R776 - Input Rate */
+ { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */
+ { 0x00000310, 0x2080 }, /* R784 - IN1L Control */
+ { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */
+ { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */
+ { 0x00000314, 0x0080 }, /* R788 - IN1R Control */
+ { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */
+ { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */
+ { 0x00000318, 0x2080 }, /* R792 - IN2L Control */
+ { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */
+ { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */
+ { 0x0000031C, 0x0080 }, /* R796 - IN2R Control */
+ { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */
+ { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */
+ { 0x00000320, 0x2080 }, /* R800 - IN3L Control */
+ { 0x00000321, 0x0180 }, /* R801 - ADC Digital Volume 3L */
+ { 0x00000322, 0x0000 }, /* R802 - DMIC3L Control */
+ { 0x00000324, 0x0080 }, /* R804 - IN3R Control */
+ { 0x00000325, 0x0180 }, /* R805 - ADC Digital Volume 3R */
+ { 0x00000326, 0x0000 }, /* R806 - DMIC3R Control */
+ { 0x00000328, 0x2000 }, /* R808 - IN4L Control */
+ { 0x00000329, 0x0180 }, /* R809 - ADC Digital Volume 4L */
+ { 0x0000032A, 0x0000 }, /* R810 - DMIC4L Control */
+ { 0x0000032D, 0x0180 }, /* R813 - ADC Digital Volume 4R */
+ { 0x0000032E, 0x0000 }, /* R814 - DMIC4R Control */
+ { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
+ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
+ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
+ { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
+ { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
+ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
+ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
+ { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
+ { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */
+ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */
+ { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */
+ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */
+ { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */
+ { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */
+ { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */
+ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */
+ { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
+ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
+ { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
+ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
+ { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */
+ { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */
+ { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */
+ { 0x00000427, 0x0020 }, /* R1063 - Noise Gate Select 3R */
+ { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
+ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
+ { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
+ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
+ { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */
+ { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */
+ { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */
+ { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */
+ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
+ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
+ { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
+ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
+ { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */
+ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
+ { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
+ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
+ { 0x00000438, 0x0000 }, /* R1080 - Output Path Config 6L */
+ { 0x00000439, 0x0180 }, /* R1081 - DAC Digital Volume 6L */
+ { 0x0000043A, 0x0080 }, /* R1082 - DAC Volume Limit 6L */
+ { 0x0000043B, 0x0400 }, /* R1083 - Noise Gate Select 6L */
+ { 0x0000043C, 0x0000 }, /* R1084 - Output Path Config 6R */
+ { 0x0000043D, 0x0180 }, /* R1085 - DAC Digital Volume 6R */
+ { 0x0000043E, 0x0080 }, /* R1086 - DAC Volume Limit 6R */
+ { 0x0000043F, 0x0800 }, /* R1087 - Noise Gate Select 6R */
+ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
+ { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
+ { 0x00000480, 0x0040 }, /* R1152 - Class W ANC Threshold 1 */
+ { 0x00000481, 0x0040 }, /* R1153 - Class W ANC Threshold 2 */
+ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
+ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
+ { 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */
+ { 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */
+ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
+ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
+ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
+ { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */
+ { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */
+ { 0x00000505, 0x0040 }, /* R1285 - AIF1 Tx BCLK Rate */
+ { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */
+ { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */
+ { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */
+ { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */
+ { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */
+ { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */
+ { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */
+ { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */
+ { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */
+ { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */
+ { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */
+ { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */
+ { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */
+ { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */
+ { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */
+ { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */
+ { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */
+ { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */
+ { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
+ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
+ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
+ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
+ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
+ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
+ { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */
+ { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */
+ { 0x00000545, 0x0040 }, /* R1349 - AIF2 Tx BCLK Rate */
+ { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */
+ { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */
+ { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */
+ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */
+ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */
+ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */
+ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
+ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
+ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
+ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
+ { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
+ { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
+ { 0x00000583, 0x0000 }, /* R1411 - AIF3 Rate Ctrl */
+ { 0x00000584, 0x0000 }, /* R1412 - AIF3 Format */
+ { 0x00000585, 0x0040 }, /* R1413 - AIF3 Tx BCLK Rate */
+ { 0x00000586, 0x0040 }, /* R1414 - AIF3 Rx BCLK Rate */
+ { 0x00000587, 0x1818 }, /* R1415 - AIF3 Frame Ctrl 1 */
+ { 0x00000588, 0x1818 }, /* R1416 - AIF3 Frame Ctrl 2 */
+ { 0x00000589, 0x0000 }, /* R1417 - AIF3 Frame Ctrl 3 */
+ { 0x0000058A, 0x0001 }, /* R1418 - AIF3 Frame Ctrl 4 */
+ { 0x00000591, 0x0000 }, /* R1425 - AIF3 Frame Ctrl 11 */
+ { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
+ { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
+ { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
+ { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
+ { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
+ { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
+ { 0x000005E7, 0x0000 }, /* R1511 - SLIMbus Rates 3 */
+ { 0x000005E8, 0x0000 }, /* R1512 - SLIMbus Rates 4 */
+ { 0x000005E9, 0x0000 }, /* R1513 - SLIMbus Rates 5 */
+ { 0x000005EA, 0x0000 }, /* R1514 - SLIMbus Rates 6 */
+ { 0x000005EB, 0x0000 }, /* R1515 - SLIMbus Rates 7 */
+ { 0x000005EC, 0x0000 }, /* R1516 - SLIMbus Rates 8 */
+ { 0x000005F5, 0x0000 }, /* R1525 - SLIMbus RX Channel Enable */
+ { 0x000005F6, 0x0000 }, /* R1526 - SLIMbus TX Channel Enable */
+ { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */
+ { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */
+ { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */
+ { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */
+ { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */
+ { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */
+ { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */
+ { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */
+ { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */
+ { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */
+ { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */
+ { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */
+ { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */
+ { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */
+ { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */
+ { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */
+ { 0x00000660, 0x0000 }, /* R1632 - MICMIX Input 1 Source */
+ { 0x00000661, 0x0080 }, /* R1633 - MICMIX Input 1 Volume */
+ { 0x00000662, 0x0000 }, /* R1634 - MICMIX Input 2 Source */
+ { 0x00000663, 0x0080 }, /* R1635 - MICMIX Input 2 Volume */
+ { 0x00000664, 0x0000 }, /* R1636 - MICMIX Input 3 Source */
+ { 0x00000665, 0x0080 }, /* R1637 - MICMIX Input 3 Volume */
+ { 0x00000666, 0x0000 }, /* R1638 - MICMIX Input 4 Source */
+ { 0x00000667, 0x0080 }, /* R1639 - MICMIX Input 4 Volume */
+ { 0x00000668, 0x0000 }, /* R1640 - NOISEMIX Input 1 Source */
+ { 0x00000669, 0x0080 }, /* R1641 - NOISEMIX Input 1 Volume */
+ { 0x0000066A, 0x0000 }, /* R1642 - NOISEMIX Input 2 Source */
+ { 0x0000066B, 0x0080 }, /* R1643 - NOISEMIX Input 2 Volume */
+ { 0x0000066C, 0x0000 }, /* R1644 - NOISEMIX Input 3 Source */
+ { 0x0000066D, 0x0080 }, /* R1645 - NOISEMIX Input 3 Volume */
+ { 0x0000066E, 0x0000 }, /* R1646 - NOISEMIX Input 4 Source */
+ { 0x0000066F, 0x0080 }, /* R1647 - NOISEMIX Input 4 Volume */
+ { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */
+ { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */
+ { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */
+ { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */
+ { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */
+ { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */
+ { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */
+ { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */
+ { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */
+ { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */
+ { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */
+ { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */
+ { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */
+ { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */
+ { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */
+ { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */
+ { 0x00000690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */
+ { 0x00000691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */
+ { 0x00000692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */
+ { 0x00000693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */
+ { 0x00000694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */
+ { 0x00000695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */
+ { 0x00000696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */
+ { 0x00000697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */
+ { 0x00000698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */
+ { 0x00000699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */
+ { 0x0000069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */
+ { 0x0000069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */
+ { 0x0000069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */
+ { 0x0000069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */
+ { 0x0000069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */
+ { 0x0000069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */
+ { 0x000006A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */
+ { 0x000006A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */
+ { 0x000006A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */
+ { 0x000006A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */
+ { 0x000006A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */
+ { 0x000006A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */
+ { 0x000006A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */
+ { 0x000006A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */
+ { 0x000006A8, 0x0000 }, /* R1704 - OUT3RMIX Input 1 Source */
+ { 0x000006A9, 0x0080 }, /* R1705 - OUT3RMIX Input 1 Volume */
+ { 0x000006AA, 0x0000 }, /* R1706 - OUT3RMIX Input 2 Source */
+ { 0x000006AB, 0x0080 }, /* R1707 - OUT3RMIX Input 2 Volume */
+ { 0x000006AC, 0x0000 }, /* R1708 - OUT3RMIX Input 3 Source */
+ { 0x000006AD, 0x0080 }, /* R1709 - OUT3RMIX Input 3 Volume */
+ { 0x000006AE, 0x0000 }, /* R1710 - OUT3RMIX Input 4 Source */
+ { 0x000006AF, 0x0080 }, /* R1711 - OUT3RMIX Input 4 Volume */
+ { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */
+ { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */
+ { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */
+ { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */
+ { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */
+ { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */
+ { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */
+ { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */
+ { 0x000006B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */
+ { 0x000006B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */
+ { 0x000006BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */
+ { 0x000006BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */
+ { 0x000006BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */
+ { 0x000006BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */
+ { 0x000006BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */
+ { 0x000006BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */
+ { 0x000006C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */
+ { 0x000006C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */
+ { 0x000006C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */
+ { 0x000006C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */
+ { 0x000006C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */
+ { 0x000006C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */
+ { 0x000006C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */
+ { 0x000006C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */
+ { 0x000006C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */
+ { 0x000006C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */
+ { 0x000006CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */
+ { 0x000006CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */
+ { 0x000006CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */
+ { 0x000006CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */
+ { 0x000006CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */
+ { 0x000006CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */
+ { 0x000006D0, 0x0000 }, /* R1744 - OUT6LMIX Input 1 Source */
+ { 0x000006D1, 0x0080 }, /* R1745 - OUT6LMIX Input 1 Volume */
+ { 0x000006D2, 0x0000 }, /* R1746 - OUT6LMIX Input 2 Source */
+ { 0x000006D3, 0x0080 }, /* R1747 - OUT6LMIX Input 2 Volume */
+ { 0x000006D4, 0x0000 }, /* R1748 - OUT6LMIX Input 3 Source */
+ { 0x000006D5, 0x0080 }, /* R1749 - OUT6LMIX Input 3 Volume */
+ { 0x000006D6, 0x0000 }, /* R1750 - OUT6LMIX Input 4 Source */
+ { 0x000006D7, 0x0080 }, /* R1751 - OUT6LMIX Input 4 Volume */
+ { 0x000006D8, 0x0000 }, /* R1752 - OUT6RMIX Input 1 Source */
+ { 0x000006D9, 0x0080 }, /* R1753 - OUT6RMIX Input 1 Volume */
+ { 0x000006DA, 0x0000 }, /* R1754 - OUT6RMIX Input 2 Source */
+ { 0x000006DB, 0x0080 }, /* R1755 - OUT6RMIX Input 2 Volume */
+ { 0x000006DC, 0x0000 }, /* R1756 - OUT6RMIX Input 3 Source */
+ { 0x000006DD, 0x0080 }, /* R1757 - OUT6RMIX Input 3 Volume */
+ { 0x000006DE, 0x0000 }, /* R1758 - OUT6RMIX Input 4 Source */
+ { 0x000006DF, 0x0080 }, /* R1759 - OUT6RMIX Input 4 Volume */
+ { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */
+ { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */
+ { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */
+ { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */
+ { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */
+ { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */
+ { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */
+ { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */
+ { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */
+ { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */
+ { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */
+ { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */
+ { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */
+ { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */
+ { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */
+ { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */
+ { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */
+ { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */
+ { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */
+ { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */
+ { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */
+ { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */
+ { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */
+ { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */
+ { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */
+ { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */
+ { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */
+ { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */
+ { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */
+ { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */
+ { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */
+ { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */
+ { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */
+ { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */
+ { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */
+ { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */
+ { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */
+ { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */
+ { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */
+ { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */
+ { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */
+ { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */
+ { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */
+ { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */
+ { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */
+ { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */
+ { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */
+ { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */
+ { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */
+ { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */
+ { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */
+ { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */
+ { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */
+ { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */
+ { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */
+ { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */
+ { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */
+ { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */
+ { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */
+ { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */
+ { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */
+ { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */
+ { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */
+ { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */
+ { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */
+ { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */
+ { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */
+ { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */
+ { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */
+ { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */
+ { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */
+ { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */
+ { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */
+ { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */
+ { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */
+ { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */
+ { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */
+ { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
+ { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
+ { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */
+ { 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */
+ { 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */
+ { 0x00000783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */
+ { 0x00000784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */
+ { 0x00000785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */
+ { 0x00000786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */
+ { 0x00000787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */
+ { 0x00000788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */
+ { 0x00000789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */
+ { 0x0000078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */
+ { 0x0000078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */
+ { 0x0000078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */
+ { 0x0000078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */
+ { 0x0000078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */
+ { 0x0000078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */
+ { 0x000007C0, 0x0000 }, /* R1984 - SLIMTX1MIX Input 1 Source */
+ { 0x000007C1, 0x0080 }, /* R1985 - SLIMTX1MIX Input 1 Volume */
+ { 0x000007C2, 0x0000 }, /* R1986 - SLIMTX1MIX Input 2 Source */
+ { 0x000007C3, 0x0080 }, /* R1987 - SLIMTX1MIX Input 2 Volume */
+ { 0x000007C4, 0x0000 }, /* R1988 - SLIMTX1MIX Input 3 Source */
+ { 0x000007C5, 0x0080 }, /* R1989 - SLIMTX1MIX Input 3 Volume */
+ { 0x000007C6, 0x0000 }, /* R1990 - SLIMTX1MIX Input 4 Source */
+ { 0x000007C7, 0x0080 }, /* R1991 - SLIMTX1MIX Input 4 Volume */
+ { 0x000007C8, 0x0000 }, /* R1992 - SLIMTX2MIX Input 1 Source */
+ { 0x000007C9, 0x0080 }, /* R1993 - SLIMTX2MIX Input 1 Volume */
+ { 0x000007CA, 0x0000 }, /* R1994 - SLIMTX2MIX Input 2 Source */
+ { 0x000007CB, 0x0080 }, /* R1995 - SLIMTX2MIX Input 2 Volume */
+ { 0x000007CC, 0x0000 }, /* R1996 - SLIMTX2MIX Input 3 Source */
+ { 0x000007CD, 0x0080 }, /* R1997 - SLIMTX2MIX Input 3 Volume */
+ { 0x000007CE, 0x0000 }, /* R1998 - SLIMTX2MIX Input 4 Source */
+ { 0x000007CF, 0x0080 }, /* R1999 - SLIMTX2MIX Input 4 Volume */
+ { 0x000007D0, 0x0000 }, /* R2000 - SLIMTX3MIX Input 1 Source */
+ { 0x000007D1, 0x0080 }, /* R2001 - SLIMTX3MIX Input 1 Volume */
+ { 0x000007D2, 0x0000 }, /* R2002 - SLIMTX3MIX Input 2 Source */
+ { 0x000007D3, 0x0080 }, /* R2003 - SLIMTX3MIX Input 2 Volume */
+ { 0x000007D4, 0x0000 }, /* R2004 - SLIMTX3MIX Input 3 Source */
+ { 0x000007D5, 0x0080 }, /* R2005 - SLIMTX3MIX Input 3 Volume */
+ { 0x000007D6, 0x0000 }, /* R2006 - SLIMTX3MIX Input 4 Source */
+ { 0x000007D7, 0x0080 }, /* R2007 - SLIMTX3MIX Input 4 Volume */
+ { 0x000007D8, 0x0000 }, /* R2008 - SLIMTX4MIX Input 1 Source */
+ { 0x000007D9, 0x0080 }, /* R2009 - SLIMTX4MIX Input 1 Volume */
+ { 0x000007DA, 0x0000 }, /* R2010 - SLIMTX4MIX Input 2 Source */
+ { 0x000007DB, 0x0080 }, /* R2011 - SLIMTX4MIX Input 2 Volume */
+ { 0x000007DC, 0x0000 }, /* R2012 - SLIMTX4MIX Input 3 Source */
+ { 0x000007DD, 0x0080 }, /* R2013 - SLIMTX4MIX Input 3 Volume */
+ { 0x000007DE, 0x0000 }, /* R2014 - SLIMTX4MIX Input 4 Source */
+ { 0x000007DF, 0x0080 }, /* R2015 - SLIMTX4MIX Input 4 Volume */
+ { 0x000007E0, 0x0000 }, /* R2016 - SLIMTX5MIX Input 1 Source */
+ { 0x000007E1, 0x0080 }, /* R2017 - SLIMTX5MIX Input 1 Volume */
+ { 0x000007E2, 0x0000 }, /* R2018 - SLIMTX5MIX Input 2 Source */
+ { 0x000007E3, 0x0080 }, /* R2019 - SLIMTX5MIX Input 2 Volume */
+ { 0x000007E4, 0x0000 }, /* R2020 - SLIMTX5MIX Input 3 Source */
+ { 0x000007E5, 0x0080 }, /* R2021 - SLIMTX5MIX Input 3 Volume */
+ { 0x000007E6, 0x0000 }, /* R2022 - SLIMTX5MIX Input 4 Source */
+ { 0x000007E7, 0x0080 }, /* R2023 - SLIMTX5MIX Input 4 Volume */
+ { 0x000007E8, 0x0000 }, /* R2024 - SLIMTX6MIX Input 1 Source */
+ { 0x000007E9, 0x0080 }, /* R2025 - SLIMTX6MIX Input 1 Volume */
+ { 0x000007EA, 0x0000 }, /* R2026 - SLIMTX6MIX Input 2 Source */
+ { 0x000007EB, 0x0080 }, /* R2027 - SLIMTX6MIX Input 2 Volume */
+ { 0x000007EC, 0x0000 }, /* R2028 - SLIMTX6MIX Input 3 Source */
+ { 0x000007ED, 0x0080 }, /* R2029 - SLIMTX6MIX Input 3 Volume */
+ { 0x000007EE, 0x0000 }, /* R2030 - SLIMTX6MIX Input 4 Source */
+ { 0x000007EF, 0x0080 }, /* R2031 - SLIMTX6MIX Input 4 Volume */
+ { 0x000007F0, 0x0000 }, /* R2032 - SLIMTX7MIX Input 1 Source */
+ { 0x000007F1, 0x0080 }, /* R2033 - SLIMTX7MIX Input 1 Volume */
+ { 0x000007F2, 0x0000 }, /* R2034 - SLIMTX7MIX Input 2 Source */
+ { 0x000007F3, 0x0080 }, /* R2035 - SLIMTX7MIX Input 2 Volume */
+ { 0x000007F4, 0x0000 }, /* R2036 - SLIMTX7MIX Input 3 Source */
+ { 0x000007F5, 0x0080 }, /* R2037 - SLIMTX7MIX Input 3 Volume */
+ { 0x000007F6, 0x0000 }, /* R2038 - SLIMTX7MIX Input 4 Source */
+ { 0x000007F7, 0x0080 }, /* R2039 - SLIMTX7MIX Input 4 Volume */
+ { 0x000007F8, 0x0000 }, /* R2040 - SLIMTX8MIX Input 1 Source */
+ { 0x000007F9, 0x0080 }, /* R2041 - SLIMTX8MIX Input 1 Volume */
+ { 0x000007FA, 0x0000 }, /* R2042 - SLIMTX8MIX Input 2 Source */
+ { 0x000007FB, 0x0080 }, /* R2043 - SLIMTX8MIX Input 2 Volume */
+ { 0x000007FC, 0x0000 }, /* R2044 - SLIMTX8MIX Input 3 Source */
+ { 0x000007FD, 0x0080 }, /* R2045 - SLIMTX8MIX Input 3 Volume */
+ { 0x000007FE, 0x0000 }, /* R2046 - SLIMTX8MIX Input 4 Source */
+ { 0x000007FF, 0x0080 }, /* R2047 - SLIMTX8MIX Input 4 Volume */
+ { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */
+ { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */
+ { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */
+ { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */
+ { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */
+ { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */
+ { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */
+ { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */
+ { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */
+ { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */
+ { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */
+ { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */
+ { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */
+ { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */
+ { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */
+ { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */
+ { 0x00000890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */
+ { 0x00000891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */
+ { 0x00000892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */
+ { 0x00000893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */
+ { 0x00000894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */
+ { 0x00000895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */
+ { 0x00000896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */
+ { 0x00000897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */
+ { 0x00000898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */
+ { 0x00000899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */
+ { 0x0000089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */
+ { 0x0000089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */
+ { 0x0000089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */
+ { 0x0000089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */
+ { 0x0000089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */
+ { 0x0000089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */
+ { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */
+ { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */
+ { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */
+ { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */
+ { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */
+ { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */
+ { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */
+ { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */
+ { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */
+ { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */
+ { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */
+ { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */
+ { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */
+ { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
+ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
+ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
+ { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
+ { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
+ { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
+ { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
+ { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
+ { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
+ { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
+ { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
+ { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
+ { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
+ { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
+ { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
+ { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
+ { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
+ { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
+ { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
+ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
+ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
+ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
+ { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */
+ { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */
+ { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */
+ { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */
+ { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */
+ { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */
+ { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */
+ { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */
+ { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */
+ { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */
+ { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */
+ { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */
+ { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */
+ { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */
+ { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */
+ { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */
+ { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */
+ { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */
+ { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */
+ { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */
+ { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */
+ { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */
+ { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */
+ { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */
+ { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */
+ { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */
+ { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */
+ { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */
+ { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */
+ { 0x00000940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */
+ { 0x00000941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */
+ { 0x00000942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */
+ { 0x00000943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */
+ { 0x00000944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */
+ { 0x00000945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */
+ { 0x00000946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */
+ { 0x00000947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */
+ { 0x00000948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */
+ { 0x00000949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */
+ { 0x0000094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */
+ { 0x0000094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */
+ { 0x0000094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */
+ { 0x0000094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */
+ { 0x0000094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */
+ { 0x0000094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */
+ { 0x00000950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */
+ { 0x00000958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */
+ { 0x00000960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */
+ { 0x00000968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */
+ { 0x00000970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */
+ { 0x00000978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */
+ { 0x00000980, 0x0000 }, /* R2432 - DSP2LMIX Input 1 Source */
+ { 0x00000981, 0x0080 }, /* R2433 - DSP2LMIX Input 1 Volume */
+ { 0x00000982, 0x0000 }, /* R2434 - DSP2LMIX Input 2 Source */
+ { 0x00000983, 0x0080 }, /* R2435 - DSP2LMIX Input 2 Volume */
+ { 0x00000984, 0x0000 }, /* R2436 - DSP2LMIX Input 3 Source */
+ { 0x00000985, 0x0080 }, /* R2437 - DSP2LMIX Input 3 Volume */
+ { 0x00000986, 0x0000 }, /* R2438 - DSP2LMIX Input 4 Source */
+ { 0x00000987, 0x0080 }, /* R2439 - DSP2LMIX Input 4 Volume */
+ { 0x00000988, 0x0000 }, /* R2440 - DSP2RMIX Input 1 Source */
+ { 0x00000989, 0x0080 }, /* R2441 - DSP2RMIX Input 1 Volume */
+ { 0x0000098A, 0x0000 }, /* R2442 - DSP2RMIX Input 2 Source */
+ { 0x0000098B, 0x0080 }, /* R2443 - DSP2RMIX Input 2 Volume */
+ { 0x0000098C, 0x0000 }, /* R2444 - DSP2RMIX Input 3 Source */
+ { 0x0000098D, 0x0080 }, /* R2445 - DSP2RMIX Input 3 Volume */
+ { 0x0000098E, 0x0000 }, /* R2446 - DSP2RMIX Input 4 Source */
+ { 0x0000098F, 0x0080 }, /* R2447 - DSP2RMIX Input 4 Volume */
+ { 0x00000990, 0x0000 }, /* R2448 - DSP2AUX1MIX Input 1 Source */
+ { 0x00000998, 0x0000 }, /* R2456 - DSP2AUX2MIX Input 1 Source */
+ { 0x000009A0, 0x0000 }, /* R2464 - DSP2AUX3MIX Input 1 Source */
+ { 0x000009A8, 0x0000 }, /* R2472 - DSP2AUX4MIX Input 1 Source */
+ { 0x000009B0, 0x0000 }, /* R2480 - DSP2AUX5MIX Input 1 Source */
+ { 0x000009B8, 0x0000 }, /* R2488 - DSP2AUX6MIX Input 1 Source */
+ { 0x000009C0, 0x0000 }, /* R2496 - DSP3LMIX Input 1 Source */
+ { 0x000009C1, 0x0080 }, /* R2497 - DSP3LMIX Input 1 Volume */
+ { 0x000009C2, 0x0000 }, /* R2498 - DSP3LMIX Input 2 Source */
+ { 0x000009C3, 0x0080 }, /* R2499 - DSP3LMIX Input 2 Volume */
+ { 0x000009C4, 0x0000 }, /* R2500 - DSP3LMIX Input 3 Source */
+ { 0x000009C5, 0x0080 }, /* R2501 - DSP3LMIX Input 3 Volume */
+ { 0x000009C6, 0x0000 }, /* R2502 - DSP3LMIX Input 4 Source */
+ { 0x000009C7, 0x0080 }, /* R2503 - DSP3LMIX Input 4 Volume */
+ { 0x000009C8, 0x0000 }, /* R2504 - DSP3RMIX Input 1 Source */
+ { 0x000009C9, 0x0080 }, /* R2505 - DSP3RMIX Input 1 Volume */
+ { 0x000009CA, 0x0000 }, /* R2506 - DSP3RMIX Input 2 Source */
+ { 0x000009CB, 0x0080 }, /* R2507 - DSP3RMIX Input 2 Volume */
+ { 0x000009CC, 0x0000 }, /* R2508 - DSP3RMIX Input 3 Source */
+ { 0x000009CD, 0x0080 }, /* R2509 - DSP3RMIX Input 3 Volume */
+ { 0x000009CE, 0x0000 }, /* R2510 - DSP3RMIX Input 4 Source */
+ { 0x000009CF, 0x0080 }, /* R2511 - DSP3RMIX Input 4 Volume */
+ { 0x000009D0, 0x0000 }, /* R2512 - DSP3AUX1MIX Input 1 Source */
+ { 0x000009D8, 0x0000 }, /* R2520 - DSP3AUX2MIX Input 1 Source */
+ { 0x000009E0, 0x0000 }, /* R2528 - DSP3AUX3MIX Input 1 Source */
+ { 0x000009E8, 0x0000 }, /* R2536 - DSP3AUX4MIX Input 1 Source */
+ { 0x000009F0, 0x0000 }, /* R2544 - DSP3AUX5MIX Input 1 Source */
+ { 0x000009F8, 0x0000 }, /* R2552 - DSP3AUX6MIX Input 1 Source */
+ { 0x00000A00, 0x0000 }, /* R2560 - DSP4LMIX Input 1 Source */
+ { 0x00000A01, 0x0080 }, /* R2561 - DSP4LMIX Input 1 Volume */
+ { 0x00000A02, 0x0000 }, /* R2562 - DSP4LMIX Input 2 Source */
+ { 0x00000A03, 0x0080 }, /* R2563 - DSP4LMIX Input 2 Volume */
+ { 0x00000A04, 0x0000 }, /* R2564 - DSP4LMIX Input 3 Source */
+ { 0x00000A05, 0x0080 }, /* R2565 - DSP4LMIX Input 3 Volume */
+ { 0x00000A06, 0x0000 }, /* R2566 - DSP4LMIX Input 4 Source */
+ { 0x00000A07, 0x0080 }, /* R2567 - DSP4LMIX Input 4 Volume */
+ { 0x00000A08, 0x0000 }, /* R2568 - DSP4RMIX Input 1 Source */
+ { 0x00000A09, 0x0080 }, /* R2569 - DSP4RMIX Input 1 Volume */
+ { 0x00000A0A, 0x0000 }, /* R2570 - DSP4RMIX Input 2 Source */
+ { 0x00000A0B, 0x0080 }, /* R2571 - DSP4RMIX Input 2 Volume */
+ { 0x00000A0C, 0x0000 }, /* R2572 - DSP4RMIX Input 3 Source */
+ { 0x00000A0D, 0x0080 }, /* R2573 - DSP4RMIX Input 3 Volume */
+ { 0x00000A0E, 0x0000 }, /* R2574 - DSP4RMIX Input 4 Source */
+ { 0x00000A0F, 0x0080 }, /* R2575 - DSP4RMIX Input 4 Volume */
+ { 0x00000A10, 0x0000 }, /* R2576 - DSP4AUX1MIX Input 1 Source */
+ { 0x00000A18, 0x0000 }, /* R2584 - DSP4AUX2MIX Input 1 Source */
+ { 0x00000A20, 0x0000 }, /* R2592 - DSP4AUX3MIX Input 1 Source */
+ { 0x00000A28, 0x0000 }, /* R2600 - DSP4AUX4MIX Input 1 Source */
+ { 0x00000A30, 0x0000 }, /* R2608 - DSP4AUX5MIX Input 1 Source */
+ { 0x00000A38, 0x0000 }, /* R2616 - DSP4AUX6MIX Input 1 Source */
+ { 0x00000A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */
+ { 0x00000A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */
+ { 0x00000A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */
+ { 0x00000A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */
+ { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */
+ { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */
+ { 0x00000B10, 0x0000 }, /* R2832 - ISRC1DEC3MIX Input 1 Source */
+ { 0x00000B18, 0x0000 }, /* R2840 - ISRC1DEC4MIX Input 1 Source */
+ { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */
+ { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */
+ { 0x00000B30, 0x0000 }, /* R2864 - ISRC1INT3MIX Input 1 Source */
+ { 0x00000B38, 0x0000 }, /* R2872 - ISRC1INT4MIX Input 1 Source */
+ { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */
+ { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */
+ { 0x00000B50, 0x0000 }, /* R2896 - ISRC2DEC3MIX Input 1 Source */
+ { 0x00000B58, 0x0000 }, /* R2904 - ISRC2DEC4MIX Input 1 Source */
+ { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */
+ { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */
+ { 0x00000B70, 0x0000 }, /* R2928 - ISRC2INT3MIX Input 1 Source */
+ { 0x00000B78, 0x0000 }, /* R2936 - ISRC2INT4MIX Input 1 Source */
+ { 0x00000B80, 0x0000 }, /* R2944 - ISRC3DEC1MIX Input 1 Source */
+ { 0x00000B88, 0x0000 }, /* R2952 - ISRC3DEC2MIX Input 1 Source */
+ { 0x00000B90, 0x0000 }, /* R2960 - ISRC3DEC3MIX Input 1 Source */
+ { 0x00000B98, 0x0000 }, /* R2968 - ISRC3DEC4MIX Input 1 Source */
+ { 0x00000BA0, 0x0000 }, /* R2976 - ISRC3INT1MIX Input 1 Source */
+ { 0x00000BA8, 0x0000 }, /* R2984 - ISRC3INT2MIX Input 1 Source */
+ { 0x00000BB0, 0x0000 }, /* R2992 - ISRC3INT3MIX Input 1 Source */
+ { 0x00000BB8, 0x0000 }, /* R3000 - ISRC3INT4MIX Input 1 Source */
+ { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */
+ { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */
+ { 0x00000C02, 0xA101 }, /* R3074 - GPIO3 CTRL */
+ { 0x00000C03, 0xA101 }, /* R3075 - GPIO4 CTRL */
+ { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */
+ { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */
+ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */
+ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */
+ { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */
+ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */
+ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */
+ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */
+ { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */
+ { 0x00000C30, 0x8282 }, /* R3120 - Misc Pad Ctrl 7 */
+ { 0x00000C31, 0x0082 }, /* R3121 - Misc Pad Ctrl 8 */
+ { 0x00000C32, 0x8282 }, /* R3122 - Misc Pad Ctrl 9 */
+ { 0x00000C33, 0x8282 }, /* R3123 - Misc Pad Ctrl 10 */
+ { 0x00000C34, 0x8282 }, /* R3124 - Misc Pad Ctrl 11 */
+ { 0x00000C35, 0x8282 }, /* R3125 - Misc Pad Ctrl 12 */
+ { 0x00000C36, 0x8282 }, /* R3126 - Misc Pad Ctrl 13 */
+ { 0x00000C37, 0x8282 }, /* R3127 - Misc Pad Ctrl 14 */
+ { 0x00000C38, 0x8282 }, /* R3128 - Misc Pad Ctrl 15 */
+ { 0x00000C39, 0x8282 }, /* R3129 - Misc Pad Ctrl 16 */
+ { 0x00000C3A, 0x8282 }, /* R3130 - Misc Pad Ctrl 17 */
+ { 0x00000C3B, 0x8282 }, /* R3131 - Misc Pad Ctrl 18 */
+ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */
+ { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */
+ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */
+ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */
+ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */
+ { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */
+ { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */
+ { 0x00000D19, 0xFFFF }, /* R3353 - IRQ2 Status 2 Mask */
+ { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */
+ { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
+ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
+ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
+ { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */
+ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
+ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
+ { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
+ { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */
+ { 0x00000E01, 0x0000 }, /* R3585 - FX_Ctrl2 */
+ { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */
+ { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */
+ { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */
+ { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */
+ { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */
+ { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */
+ { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */
+ { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */
+ { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */
+ { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */
+ { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */
+ { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */
+ { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */
+ { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */
+ { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */
+ { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */
+ { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */
+ { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */
+ { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */
+ { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */
+ { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */
+ { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */
+ { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */
+ { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */
+ { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */
+ { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */
+ { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */
+ { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */
+ { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */
+ { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */
+ { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */
+ { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */
+ { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */
+ { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */
+ { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */
+ { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */
+ { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */
+ { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */
+ { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */
+ { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */
+ { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */
+ { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */
+ { 0x00000E3C, 0x6318 }, /* R3644 - EQ3_1 */
+ { 0x00000E3D, 0x6300 }, /* R3645 - EQ3_2 */
+ { 0x00000E3E, 0x0FC8 }, /* R3646 - EQ3_3 */
+ { 0x00000E3F, 0x03FE }, /* R3647 - EQ3_4 */
+ { 0x00000E40, 0x00E0 }, /* R3648 - EQ3_5 */
+ { 0x00000E41, 0x1EC4 }, /* R3649 - EQ3_6 */
+ { 0x00000E42, 0xF136 }, /* R3650 - EQ3_7 */
+ { 0x00000E43, 0x0409 }, /* R3651 - EQ3_8 */
+ { 0x00000E44, 0x04CC }, /* R3652 - EQ3_9 */
+ { 0x00000E45, 0x1C9B }, /* R3653 - EQ3_10 */
+ { 0x00000E46, 0xF337 }, /* R3654 - EQ3_11 */
+ { 0x00000E47, 0x040B }, /* R3655 - EQ3_12 */
+ { 0x00000E48, 0x0CBB }, /* R3656 - EQ3_13 */
+ { 0x00000E49, 0x16F8 }, /* R3657 - EQ3_14 */
+ { 0x00000E4A, 0xF7D9 }, /* R3658 - EQ3_15 */
+ { 0x00000E4B, 0x040A }, /* R3659 - EQ3_16 */
+ { 0x00000E4C, 0x1F14 }, /* R3660 - EQ3_17 */
+ { 0x00000E4D, 0x058C }, /* R3661 - EQ3_18 */
+ { 0x00000E4E, 0x0563 }, /* R3662 - EQ3_19 */
+ { 0x00000E4F, 0x4000 }, /* R3663 - EQ3_20 */
+ { 0x00000E50, 0x0B75 }, /* R3664 - EQ3_21 */
+ { 0x00000E52, 0x6318 }, /* R3666 - EQ4_1 */
+ { 0x00000E53, 0x6300 }, /* R3667 - EQ4_2 */
+ { 0x00000E54, 0x0FC8 }, /* R3668 - EQ4_3 */
+ { 0x00000E55, 0x03FE }, /* R3669 - EQ4_4 */
+ { 0x00000E56, 0x00E0 }, /* R3670 - EQ4_5 */
+ { 0x00000E57, 0x1EC4 }, /* R3671 - EQ4_6 */
+ { 0x00000E58, 0xF136 }, /* R3672 - EQ4_7 */
+ { 0x00000E59, 0x0409 }, /* R3673 - EQ4_8 */
+ { 0x00000E5A, 0x04CC }, /* R3674 - EQ4_9 */
+ { 0x00000E5B, 0x1C9B }, /* R3675 - EQ4_10 */
+ { 0x00000E5C, 0xF337 }, /* R3676 - EQ4_11 */
+ { 0x00000E5D, 0x040B }, /* R3677 - EQ4_12 */
+ { 0x00000E5E, 0x0CBB }, /* R3678 - EQ4_13 */
+ { 0x00000E5F, 0x16F8 }, /* R3679 - EQ4_14 */
+ { 0x00000E60, 0xF7D9 }, /* R3680 - EQ4_15 */
+ { 0x00000E61, 0x040A }, /* R3681 - EQ4_16 */
+ { 0x00000E62, 0x1F14 }, /* R3682 - EQ4_17 */
+ { 0x00000E63, 0x058C }, /* R3683 - EQ4_18 */
+ { 0x00000E64, 0x0563 }, /* R3684 - EQ4_19 */
+ { 0x00000E65, 0x4000 }, /* R3685 - EQ4_20 */
+ { 0x00000E66, 0x0B75 }, /* R3686 - EQ4_21 */
+ { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */
+ { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */
+ { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
+ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
+ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
+ { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
+ { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
+ { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
+ { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
+ { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
+ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
+ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
+ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
+ { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */
+ { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */
+ { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
+ { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
+ { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
+ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
+ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
+ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
+ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
+ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
+ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
+ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
+ { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
+ { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
+ { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
+ { 0x00000F00, 0x0000 }, /* R3840 - Clock Control */
+ { 0x00000F01, 0x0000 }, /* R3841 - ANC_SRC */
+ { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
+ { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
+ { 0x00001200, 0x0010 }, /* R4608 - DSP2 Control 1 */
+ { 0x00001201, 0x0000 }, /* R4609 - DSP2 Clocking 1 */
+ { 0x00001300, 0x0010 }, /* R4864 - DSP3 Control 1 */
+ { 0x00001301, 0x0000 }, /* R4865 - DSP3 Clocking 1 */
+ { 0x00001400, 0x0010 }, /* R5120 - DSP4 Control 1 */
+ { 0x00001401, 0x0000 }, /* R5121 - DSP4 Clocking 1 */
+ { 0x00001404, 0x0000 }, /* R5124 - DSP4 Status 1 */
+};
+
+static bool wm5110_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_CTRL_IF_SPI_CFG_1:
+ case ARIZONA_CTRL_IF_I2C1_CFG_1:
+ case ARIZONA_CTRL_IF_I2C2_CFG_1:
+ case ARIZONA_CTRL_IF_I2C1_CFG_2:
+ case ARIZONA_CTRL_IF_I2C2_CFG_2:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+ case ARIZONA_TONE_GENERATOR_1:
+ case ARIZONA_TONE_GENERATOR_2:
+ case ARIZONA_TONE_GENERATOR_3:
+ case ARIZONA_TONE_GENERATOR_4:
+ case ARIZONA_TONE_GENERATOR_5:
+ case ARIZONA_PWM_DRIVE_1:
+ case ARIZONA_PWM_DRIVE_2:
+ case ARIZONA_PWM_DRIVE_3:
+ case ARIZONA_WAKE_CONTROL:
+ case ARIZONA_SEQUENCE_CONTROL:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3:
+ case ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4:
+ case ARIZONA_COMFORT_NOISE_GENERATOR:
+ case ARIZONA_HAPTICS_CONTROL_1:
+ case ARIZONA_HAPTICS_CONTROL_2:
+ case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_1_DURATION:
+ case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_2_DURATION:
+ case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_3_DURATION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_CLOCK_32K_1:
+ case ARIZONA_SYSTEM_CLOCK_1:
+ case ARIZONA_SAMPLE_RATE_1:
+ case ARIZONA_SAMPLE_RATE_2:
+ case ARIZONA_SAMPLE_RATE_3:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_CLOCK_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+ case ARIZONA_OUTPUT_ASYNC_CLOCK:
+ case ARIZONA_RATE_ESTIMATOR_1:
+ case ARIZONA_RATE_ESTIMATOR_2:
+ case ARIZONA_RATE_ESTIMATOR_3:
+ case ARIZONA_RATE_ESTIMATOR_4:
+ case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_FLL1_CONTROL_1:
+ case ARIZONA_FLL1_CONTROL_2:
+ case ARIZONA_FLL1_CONTROL_3:
+ case ARIZONA_FLL1_CONTROL_4:
+ case ARIZONA_FLL1_CONTROL_5:
+ case ARIZONA_FLL1_CONTROL_6:
+ case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL1_SYNCHRONISER_1:
+ case ARIZONA_FLL1_SYNCHRONISER_2:
+ case ARIZONA_FLL1_SYNCHRONISER_3:
+ case ARIZONA_FLL1_SYNCHRONISER_4:
+ case ARIZONA_FLL1_SYNCHRONISER_5:
+ case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SPREAD_SPECTRUM:
+ case ARIZONA_FLL1_GPIO_CLOCK:
+ case ARIZONA_FLL2_CONTROL_1:
+ case ARIZONA_FLL2_CONTROL_2:
+ case ARIZONA_FLL2_CONTROL_3:
+ case ARIZONA_FLL2_CONTROL_4:
+ case ARIZONA_FLL2_CONTROL_5:
+ case ARIZONA_FLL2_CONTROL_6:
+ case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL2_NCO_TEST_0:
+ case ARIZONA_FLL2_SYNCHRONISER_1:
+ case ARIZONA_FLL2_SYNCHRONISER_2:
+ case ARIZONA_FLL2_SYNCHRONISER_3:
+ case ARIZONA_FLL2_SYNCHRONISER_4:
+ case ARIZONA_FLL2_SYNCHRONISER_5:
+ case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SPREAD_SPECTRUM:
+ case ARIZONA_FLL2_GPIO_CLOCK:
+ case ARIZONA_MIC_CHARGE_PUMP_1:
+ case ARIZONA_LDO1_CONTROL_1:
+ case ARIZONA_LDO2_CONTROL_1:
+ case ARIZONA_MIC_BIAS_CTRL_1:
+ case ARIZONA_MIC_BIAS_CTRL_2:
+ case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_ACCESSORY_DETECT_MODE_1:
+ case ARIZONA_HEADPHONE_DETECT_1:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_MIC_DETECT_1:
+ case ARIZONA_MIC_DETECT_2:
+ case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
+ case ARIZONA_JACK_DETECT_ANALOGUE:
+ case ARIZONA_INPUT_ENABLES:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_INPUT_RATE:
+ case ARIZONA_INPUT_VOLUME_RAMP:
+ case ARIZONA_IN1L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DMIC1L_CONTROL:
+ case ARIZONA_IN1R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DMIC1R_CONTROL:
+ case ARIZONA_IN2L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DMIC2L_CONTROL:
+ case ARIZONA_IN2R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DMIC2R_CONTROL:
+ case ARIZONA_IN3L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_3L:
+ case ARIZONA_DMIC3L_CONTROL:
+ case ARIZONA_IN3R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_3R:
+ case ARIZONA_DMIC3R_CONTROL:
+ case ARIZONA_IN4L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_4L:
+ case ARIZONA_DMIC4L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_4R:
+ case ARIZONA_DMIC4R_CONTROL:
+ case ARIZONA_OUTPUT_ENABLES_1:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_OUTPUT_RATE_1:
+ case ARIZONA_OUTPUT_VOLUME_RAMP:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DAC_VOLUME_LIMIT_1L:
+ case ARIZONA_NOISE_GATE_SELECT_1L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DAC_VOLUME_LIMIT_1R:
+ case ARIZONA_NOISE_GATE_SELECT_1R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_2L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DAC_VOLUME_LIMIT_2L:
+ case ARIZONA_NOISE_GATE_SELECT_2L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_2R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DAC_VOLUME_LIMIT_2R:
+ case ARIZONA_NOISE_GATE_SELECT_2R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_3L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_3L:
+ case ARIZONA_DAC_VOLUME_LIMIT_3L:
+ case ARIZONA_NOISE_GATE_SELECT_3L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_3R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_3R:
+ case ARIZONA_DAC_VOLUME_LIMIT_3R:
+ case ARIZONA_NOISE_GATE_SELECT_3R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_4L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+ case ARIZONA_OUT_VOLUME_4L:
+ case ARIZONA_NOISE_GATE_SELECT_4L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_4R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4R:
+ case ARIZONA_OUT_VOLUME_4R:
+ case ARIZONA_NOISE_GATE_SELECT_4R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_5L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5L:
+ case ARIZONA_DAC_VOLUME_LIMIT_5L:
+ case ARIZONA_NOISE_GATE_SELECT_5L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_5R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_5R:
+ case ARIZONA_DAC_VOLUME_LIMIT_5R:
+ case ARIZONA_NOISE_GATE_SELECT_5R:
+ case ARIZONA_OUTPUT_PATH_CONFIG_6L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_6L:
+ case ARIZONA_DAC_VOLUME_LIMIT_6L:
+ case ARIZONA_NOISE_GATE_SELECT_6L:
+ case ARIZONA_OUTPUT_PATH_CONFIG_6R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_6R:
+ case ARIZONA_DAC_VOLUME_LIMIT_6R:
+ case ARIZONA_NOISE_GATE_SELECT_6R:
+ case ARIZONA_DAC_AEC_CONTROL_1:
+ case ARIZONA_NOISE_GATE_CONTROL:
+ case ARIZONA_PDM_SPK1_CTRL_1:
+ case ARIZONA_PDM_SPK1_CTRL_2:
+ case ARIZONA_PDM_SPK2_CTRL_1:
+ case ARIZONA_PDM_SPK2_CTRL_2:
+ case ARIZONA_AIF1_BCLK_CTRL:
+ case ARIZONA_AIF1_TX_PIN_CTRL:
+ case ARIZONA_AIF1_RX_PIN_CTRL:
+ case ARIZONA_AIF1_RATE_CTRL:
+ case ARIZONA_AIF1_FORMAT:
+ case ARIZONA_AIF1_TX_BCLK_RATE:
+ case ARIZONA_AIF1_RX_BCLK_RATE:
+ case ARIZONA_AIF1_FRAME_CTRL_1:
+ case ARIZONA_AIF1_FRAME_CTRL_2:
+ case ARIZONA_AIF1_FRAME_CTRL_3:
+ case ARIZONA_AIF1_FRAME_CTRL_4:
+ case ARIZONA_AIF1_FRAME_CTRL_5:
+ case ARIZONA_AIF1_FRAME_CTRL_6:
+ case ARIZONA_AIF1_FRAME_CTRL_7:
+ case ARIZONA_AIF1_FRAME_CTRL_8:
+ case ARIZONA_AIF1_FRAME_CTRL_9:
+ case ARIZONA_AIF1_FRAME_CTRL_10:
+ case ARIZONA_AIF1_FRAME_CTRL_11:
+ case ARIZONA_AIF1_FRAME_CTRL_12:
+ case ARIZONA_AIF1_FRAME_CTRL_13:
+ case ARIZONA_AIF1_FRAME_CTRL_14:
+ case ARIZONA_AIF1_FRAME_CTRL_15:
+ case ARIZONA_AIF1_FRAME_CTRL_16:
+ case ARIZONA_AIF1_FRAME_CTRL_17:
+ case ARIZONA_AIF1_FRAME_CTRL_18:
+ case ARIZONA_AIF1_TX_ENABLES:
+ case ARIZONA_AIF1_RX_ENABLES:
+ case ARIZONA_AIF2_BCLK_CTRL:
+ case ARIZONA_AIF2_TX_PIN_CTRL:
+ case ARIZONA_AIF2_RX_PIN_CTRL:
+ case ARIZONA_AIF2_RATE_CTRL:
+ case ARIZONA_AIF2_FORMAT:
+ case ARIZONA_AIF2_TX_BCLK_RATE:
+ case ARIZONA_AIF2_RX_BCLK_RATE:
+ case ARIZONA_AIF2_FRAME_CTRL_1:
+ case ARIZONA_AIF2_FRAME_CTRL_2:
+ case ARIZONA_AIF2_FRAME_CTRL_3:
+ case ARIZONA_AIF2_FRAME_CTRL_4:
+ case ARIZONA_AIF2_FRAME_CTRL_11:
+ case ARIZONA_AIF2_FRAME_CTRL_12:
+ case ARIZONA_AIF2_TX_ENABLES:
+ case ARIZONA_AIF2_RX_ENABLES:
+ case ARIZONA_AIF3_BCLK_CTRL:
+ case ARIZONA_AIF3_TX_PIN_CTRL:
+ case ARIZONA_AIF3_RX_PIN_CTRL:
+ case ARIZONA_AIF3_RATE_CTRL:
+ case ARIZONA_AIF3_FORMAT:
+ case ARIZONA_AIF3_TX_BCLK_RATE:
+ case ARIZONA_AIF3_RX_BCLK_RATE:
+ case ARIZONA_AIF3_FRAME_CTRL_1:
+ case ARIZONA_AIF3_FRAME_CTRL_2:
+ case ARIZONA_AIF3_FRAME_CTRL_3:
+ case ARIZONA_AIF3_FRAME_CTRL_4:
+ case ARIZONA_AIF3_FRAME_CTRL_11:
+ case ARIZONA_AIF3_FRAME_CTRL_12:
+ case ARIZONA_AIF3_TX_ENABLES:
+ case ARIZONA_AIF3_RX_ENABLES:
+ case ARIZONA_SLIMBUS_FRAMER_REF_GEAR:
+ case ARIZONA_SLIMBUS_RATES_1:
+ case ARIZONA_SLIMBUS_RATES_2:
+ case ARIZONA_SLIMBUS_RATES_3:
+ case ARIZONA_SLIMBUS_RATES_4:
+ case ARIZONA_SLIMBUS_RATES_5:
+ case ARIZONA_SLIMBUS_RATES_6:
+ case ARIZONA_SLIMBUS_RATES_7:
+ case ARIZONA_SLIMBUS_RATES_8:
+ case ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+ case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+ case ARIZONA_MICMIX_INPUT_1_SOURCE:
+ case ARIZONA_MICMIX_INPUT_1_VOLUME:
+ case ARIZONA_MICMIX_INPUT_2_SOURCE:
+ case ARIZONA_MICMIX_INPUT_2_VOLUME:
+ case ARIZONA_MICMIX_INPUT_3_SOURCE:
+ case ARIZONA_MICMIX_INPUT_3_VOLUME:
+ case ARIZONA_MICMIX_INPUT_4_SOURCE:
+ case ARIZONA_MICMIX_INPUT_4_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_1_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_1_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_2_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_2_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_3_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_3_VOLUME:
+ case ARIZONA_NOISEMIX_INPUT_4_SOURCE:
+ case ARIZONA_NOISEMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT3LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT3LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT3RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT3RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT3RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT3RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT3RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT3RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT3RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT3RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT5RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT5RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT6LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT6LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT6LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT6LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT6LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT6LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT6LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT6LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT6RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT6RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT6RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT6RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT6RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT6RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT6RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT6RMIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ3MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ3MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ4MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP4LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP4LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP4LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP4LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP4LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP4LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP4RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP4RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP4RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP4RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP4RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP4RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP4RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_GPIO1_CTRL:
+ case ARIZONA_GPIO2_CTRL:
+ case ARIZONA_GPIO3_CTRL:
+ case ARIZONA_GPIO4_CTRL:
+ case ARIZONA_GPIO5_CTRL:
+ case ARIZONA_IRQ_CTRL_1:
+ case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+ case ARIZONA_MISC_PAD_CTRL_1:
+ case ARIZONA_MISC_PAD_CTRL_2:
+ case ARIZONA_MISC_PAD_CTRL_3:
+ case ARIZONA_MISC_PAD_CTRL_4:
+ case ARIZONA_MISC_PAD_CTRL_5:
+ case ARIZONA_MISC_PAD_CTRL_6:
+ case ARIZONA_MISC_PAD_CTRL_7:
+ case ARIZONA_MISC_PAD_CTRL_8:
+ case ARIZONA_MISC_PAD_CTRL_9:
+ case ARIZONA_MISC_PAD_CTRL_10:
+ case ARIZONA_MISC_PAD_CTRL_11:
+ case ARIZONA_MISC_PAD_CTRL_12:
+ case ARIZONA_MISC_PAD_CTRL_13:
+ case ARIZONA_MISC_PAD_CTRL_14:
+ case ARIZONA_MISC_PAD_CTRL_15:
+ case ARIZONA_MISC_PAD_CTRL_16:
+ case ARIZONA_MISC_PAD_CTRL_17:
+ case ARIZONA_MISC_PAD_CTRL_18:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_INTERRUPT_STATUS_1_MASK:
+ case ARIZONA_INTERRUPT_STATUS_2_MASK:
+ case ARIZONA_INTERRUPT_STATUS_3_MASK:
+ case ARIZONA_INTERRUPT_STATUS_4_MASK:
+ case ARIZONA_INTERRUPT_STATUS_5_MASK:
+ case ARIZONA_INTERRUPT_CONTROL:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1_MASK:
+ case ARIZONA_IRQ2_STATUS_2_MASK:
+ case ARIZONA_IRQ2_STATUS_3_MASK:
+ case ARIZONA_IRQ2_STATUS_4_MASK:
+ case ARIZONA_IRQ2_STATUS_5_MASK:
+ case ARIZONA_IRQ2_CONTROL:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_WKUP_AND_TRIG:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_AOD_IRQ_MASK_IRQ1:
+ case ARIZONA_AOD_IRQ_MASK_IRQ2:
+ case ARIZONA_AOD_IRQ_RAW_STATUS:
+ case ARIZONA_JACK_DETECT_DEBOUNCE:
+ case ARIZONA_FX_CTRL1:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_EQ1_1:
+ case ARIZONA_EQ1_2:
+ case ARIZONA_EQ1_3:
+ case ARIZONA_EQ1_4:
+ case ARIZONA_EQ1_5:
+ case ARIZONA_EQ1_6:
+ case ARIZONA_EQ1_7:
+ case ARIZONA_EQ1_8:
+ case ARIZONA_EQ1_9:
+ case ARIZONA_EQ1_10:
+ case ARIZONA_EQ1_11:
+ case ARIZONA_EQ1_12:
+ case ARIZONA_EQ1_13:
+ case ARIZONA_EQ1_14:
+ case ARIZONA_EQ1_15:
+ case ARIZONA_EQ1_16:
+ case ARIZONA_EQ1_17:
+ case ARIZONA_EQ1_18:
+ case ARIZONA_EQ1_19:
+ case ARIZONA_EQ1_20:
+ case ARIZONA_EQ1_21:
+ case ARIZONA_EQ2_1:
+ case ARIZONA_EQ2_2:
+ case ARIZONA_EQ2_3:
+ case ARIZONA_EQ2_4:
+ case ARIZONA_EQ2_5:
+ case ARIZONA_EQ2_6:
+ case ARIZONA_EQ2_7:
+ case ARIZONA_EQ2_8:
+ case ARIZONA_EQ2_9:
+ case ARIZONA_EQ2_10:
+ case ARIZONA_EQ2_11:
+ case ARIZONA_EQ2_12:
+ case ARIZONA_EQ2_13:
+ case ARIZONA_EQ2_14:
+ case ARIZONA_EQ2_15:
+ case ARIZONA_EQ2_16:
+ case ARIZONA_EQ2_17:
+ case ARIZONA_EQ2_18:
+ case ARIZONA_EQ2_19:
+ case ARIZONA_EQ2_20:
+ case ARIZONA_EQ2_21:
+ case ARIZONA_EQ3_1:
+ case ARIZONA_EQ3_2:
+ case ARIZONA_EQ3_3:
+ case ARIZONA_EQ3_4:
+ case ARIZONA_EQ3_5:
+ case ARIZONA_EQ3_6:
+ case ARIZONA_EQ3_7:
+ case ARIZONA_EQ3_8:
+ case ARIZONA_EQ3_9:
+ case ARIZONA_EQ3_10:
+ case ARIZONA_EQ3_11:
+ case ARIZONA_EQ3_12:
+ case ARIZONA_EQ3_13:
+ case ARIZONA_EQ3_14:
+ case ARIZONA_EQ3_15:
+ case ARIZONA_EQ3_16:
+ case ARIZONA_EQ3_17:
+ case ARIZONA_EQ3_18:
+ case ARIZONA_EQ3_19:
+ case ARIZONA_EQ3_20:
+ case ARIZONA_EQ3_21:
+ case ARIZONA_EQ4_1:
+ case ARIZONA_EQ4_2:
+ case ARIZONA_EQ4_3:
+ case ARIZONA_EQ4_4:
+ case ARIZONA_EQ4_5:
+ case ARIZONA_EQ4_6:
+ case ARIZONA_EQ4_7:
+ case ARIZONA_EQ4_8:
+ case ARIZONA_EQ4_9:
+ case ARIZONA_EQ4_10:
+ case ARIZONA_EQ4_11:
+ case ARIZONA_EQ4_12:
+ case ARIZONA_EQ4_13:
+ case ARIZONA_EQ4_14:
+ case ARIZONA_EQ4_15:
+ case ARIZONA_EQ4_16:
+ case ARIZONA_EQ4_17:
+ case ARIZONA_EQ4_18:
+ case ARIZONA_EQ4_19:
+ case ARIZONA_EQ4_20:
+ case ARIZONA_EQ4_21:
+ case ARIZONA_DRC1_CTRL1:
+ case ARIZONA_DRC1_CTRL2:
+ case ARIZONA_DRC1_CTRL3:
+ case ARIZONA_DRC1_CTRL4:
+ case ARIZONA_DRC1_CTRL5:
+ case ARIZONA_DRC2_CTRL1:
+ case ARIZONA_DRC2_CTRL2:
+ case ARIZONA_DRC2_CTRL3:
+ case ARIZONA_DRC2_CTRL4:
+ case ARIZONA_DRC2_CTRL5:
+ case ARIZONA_HPLPF1_1:
+ case ARIZONA_HPLPF1_2:
+ case ARIZONA_HPLPF2_1:
+ case ARIZONA_HPLPF2_2:
+ case ARIZONA_HPLPF3_1:
+ case ARIZONA_HPLPF3_2:
+ case ARIZONA_HPLPF4_1:
+ case ARIZONA_HPLPF4_2:
+ case ARIZONA_ASRC_ENABLE:
+ case ARIZONA_ASRC_STATUS:
+ case ARIZONA_ASRC_RATE1:
+ case ARIZONA_ISRC_1_CTRL_1:
+ case ARIZONA_ISRC_1_CTRL_2:
+ case ARIZONA_ISRC_1_CTRL_3:
+ case ARIZONA_ISRC_2_CTRL_1:
+ case ARIZONA_ISRC_2_CTRL_2:
+ case ARIZONA_ISRC_2_CTRL_3:
+ case ARIZONA_ISRC_3_CTRL_1:
+ case ARIZONA_ISRC_3_CTRL_2:
+ case ARIZONA_ISRC_3_CTRL_3:
+ case ARIZONA_CLOCK_CONTROL:
+ case ARIZONA_ANC_SRC:
+ case ARIZONA_DSP_STATUS:
+ case ARIZONA_DSP1_CONTROL_1:
+ case ARIZONA_DSP1_CLOCKING_1:
+ case ARIZONA_DSP1_STATUS_1:
+ case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP2_CONTROL_1:
+ case ARIZONA_DSP2_CLOCKING_1:
+ case ARIZONA_DSP2_STATUS_1:
+ case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP3_CONTROL_1:
+ case ARIZONA_DSP3_CLOCKING_1:
+ case ARIZONA_DSP3_STATUS_1:
+ case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP4_CONTROL_1:
+ case ARIZONA_DSP4_CLOCKING_1:
+ case ARIZONA_DSP4_STATUS_1:
+ case ARIZONA_DSP4_STATUS_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_HEADPHONE_DETECT_2:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_SLIMBUS_RX_PORT_STATUS:
+ case ARIZONA_SLIMBUS_TX_PORT_STATUS:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_AOD_IRQ1:
+ case ARIZONA_AOD_IRQ2:
+ case ARIZONA_ASRC_STATUS:
+ case ARIZONA_DSP_STATUS:
+ case ARIZONA_DSP1_CONTROL_1:
+ case ARIZONA_DSP1_CLOCKING_1:
+ case ARIZONA_DSP1_STATUS_1:
+ case ARIZONA_DSP1_STATUS_2:
+ case ARIZONA_DSP2_STATUS_1:
+ case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP3_STATUS_1:
+ case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP4_STATUS_1:
+ case ARIZONA_DSP4_STATUS_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config wm5110_spi_regmap = {
+ .reg_bits = 32,
+ .pad_bits = 16,
+ .val_bits = 16,
+
+ .max_register = ARIZONA_DSP1_STATUS_2,
+ .readable_reg = wm5110_readable_register,
+ .volatile_reg = wm5110_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm5110_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5110_spi_regmap);
+
+const struct regmap_config wm5110_i2c_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+
+ .max_register = ARIZONA_DSP1_STATUS_2,
+ .readable_reg = wm5110_readable_register,
+ .volatile_reg = wm5110_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = wm5110_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
+};
+EXPORT_SYMBOL_GPL(wm5110_i2c_regmap);
diff --git a/drivers/mfd/wm831x-otp.c b/drivers/mfd/wm831x-otp.c
index f742745ff354..b90f3e06b6c9 100644
--- a/drivers/mfd/wm831x-otp.c
+++ b/drivers/mfd/wm831x-otp.c
@@ -18,6 +18,7 @@
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
+#include <linux/random.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/otp.h>
@@ -66,6 +67,7 @@ static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
int wm831x_otp_init(struct wm831x *wm831x)
{
+ char uuid[WM831X_UNIQUE_ID_LEN];
int ret;
ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
@@ -73,6 +75,12 @@ int wm831x_otp_init(struct wm831x *wm831x)
dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
ret);
+ ret = wm831x_unique_id_read(wm831x, uuid);
+ if (ret == 0)
+ add_device_randomness(uuid, sizeof(uuid));
+ else
+ dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret);
+
return ret;
}
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 8a9b11ca076a..7c1ae24605d9 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -32,9 +32,6 @@
#include <linux/mfd/wm8350/supply.h>
#include <linux/mfd/wm8350/wdt.h>
-#define WM8350_UNLOCK_KEY 0x0013
-#define WM8350_LOCK_KEY 0x0000
-
#define WM8350_CLOCK_CONTROL_1 0x28
#define WM8350_AIF_TEST 0x74
@@ -63,181 +60,32 @@
/*
* WM8350 Device IO
*/
-static DEFINE_MUTEX(io_mutex);
static DEFINE_MUTEX(reg_lock_mutex);
-/* Perform a physical read from the device.
- */
-static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
- u16 *dest)
-{
- int i, ret;
- int bytes = num_regs * 2;
-
- dev_dbg(wm8350->dev, "volatile read\n");
- ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes);
-
- for (i = reg; i < reg + num_regs; i++) {
- /* Cache is CPU endian */
- dest[i - reg] = be16_to_cpu(dest[i - reg]);
-
- /* Mask out non-readable bits */
- dest[i - reg] &= wm8350_reg_io_map[i].readable;
- }
-
- dump(num_regs, dest);
-
- return ret;
-}
-
-static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
-{
- int i;
- int end = reg + num_regs;
- int ret = 0;
- int bytes = num_regs * 2;
-
- if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
- dev_err(wm8350->dev, "invalid reg %x\n",
- reg + num_regs - 1);
- return -EINVAL;
- }
-
- dev_dbg(wm8350->dev,
- "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
-
-#if WM8350_BUS_DEBUG
- /* we can _safely_ read any register, but warn if read not supported */
- for (i = reg; i < end; i++) {
- if (!wm8350_reg_io_map[i].readable)
- dev_warn(wm8350->dev,
- "reg R%d is not readable\n", i);
- }
-#endif
-
- /* if any volatile registers are required, then read back all */
- for (i = reg; i < end; i++)
- if (wm8350_reg_io_map[i].vol)
- return wm8350_phys_read(wm8350, reg, num_regs, dest);
-
- /* no volatiles, then cache is good */
- dev_dbg(wm8350->dev, "cache read\n");
- memcpy(dest, &wm8350->reg_cache[reg], bytes);
- dump(num_regs, dest);
- return ret;
-}
-
-static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
-{
- if (reg == WM8350_SECURITY ||
- wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
- return 0;
-
- if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
- reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
- (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
- reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
- return 1;
- return 0;
-}
-
-static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
-{
- int i;
- int end = reg + num_regs;
- int bytes = num_regs * 2;
-
- if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
- dev_err(wm8350->dev, "invalid reg %x\n",
- reg + num_regs - 1);
- return -EINVAL;
- }
-
- /* it's generally not a good idea to write to RO or locked registers */
- for (i = reg; i < end; i++) {
- if (!wm8350_reg_io_map[i].writable) {
- dev_err(wm8350->dev,
- "attempted write to read only reg R%d\n", i);
- return -EINVAL;
- }
-
- if (is_reg_locked(wm8350, i)) {
- dev_err(wm8350->dev,
- "attempted write to locked reg R%d\n", i);
- return -EINVAL;
- }
-
- src[i - reg] &= wm8350_reg_io_map[i].writable;
-
- wm8350->reg_cache[i] =
- (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
- | src[i - reg];
-
- src[i - reg] = cpu_to_be16(src[i - reg]);
- }
-
- /* Actually write it out */
- return regmap_raw_write(wm8350->regmap, reg, src, bytes);
-}
-
/*
* Safe read, modify, write methods
*/
int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
{
- u16 data;
- int err;
-
- mutex_lock(&io_mutex);
- err = wm8350_read(wm8350, reg, 1, &data);
- if (err) {
- dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
- goto out;
- }
-
- data &= ~mask;
- err = wm8350_write(wm8350, reg, 1, &data);
- if (err)
- dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
-out:
- mutex_unlock(&io_mutex);
- return err;
+ return regmap_update_bits(wm8350->regmap, reg, mask, 0);
}
EXPORT_SYMBOL_GPL(wm8350_clear_bits);
int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
{
- u16 data;
- int err;
-
- mutex_lock(&io_mutex);
- err = wm8350_read(wm8350, reg, 1, &data);
- if (err) {
- dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
- goto out;
- }
-
- data |= mask;
- err = wm8350_write(wm8350, reg, 1, &data);
- if (err)
- dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
-out:
- mutex_unlock(&io_mutex);
- return err;
+ return regmap_update_bits(wm8350->regmap, reg, mask, mask);
}
EXPORT_SYMBOL_GPL(wm8350_set_bits);
u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
{
- u16 data;
+ unsigned int data;
int err;
- mutex_lock(&io_mutex);
- err = wm8350_read(wm8350, reg, 1, &data);
+ err = regmap_read(wm8350->regmap, reg, &data);
if (err)
dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
- mutex_unlock(&io_mutex);
return data;
}
EXPORT_SYMBOL_GPL(wm8350_reg_read);
@@ -245,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read);
int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
{
int ret;
- u16 data = val;
- mutex_lock(&io_mutex);
- ret = wm8350_write(wm8350, reg, 1, &data);
+ ret = regmap_write(wm8350->regmap, reg, val);
+
if (ret)
dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
- mutex_unlock(&io_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_reg_write);
@@ -261,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
{
int err = 0;
- mutex_lock(&io_mutex);
- err = wm8350_read(wm8350, start_reg, regs, dest);
+ err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs);
if (err)
dev_err(wm8350->dev, "block read starting from R%d failed\n",
start_reg);
- mutex_unlock(&io_mutex);
+
return err;
}
EXPORT_SYMBOL_GPL(wm8350_block_read);
@@ -276,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
{
int ret = 0;
- mutex_lock(&io_mutex);
- ret = wm8350_write(wm8350, start_reg, regs, src);
+ ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs);
if (ret)
dev_err(wm8350->dev, "block write starting at R%d failed\n",
start_reg);
- mutex_unlock(&io_mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_block_write);
@@ -295,15 +139,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write);
*/
int wm8350_reg_lock(struct wm8350 *wm8350)
{
- u16 key = WM8350_LOCK_KEY;
int ret;
+ mutex_lock(&reg_lock_mutex);
+
ldbg(__func__);
- mutex_lock(&io_mutex);
- ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+
+ ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY);
if (ret)
dev_err(wm8350->dev, "lock failed\n");
- mutex_unlock(&io_mutex);
+
+ wm8350->unlocked = false;
+
+ mutex_unlock(&reg_lock_mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_reg_lock);
@@ -319,15 +168,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock);
*/
int wm8350_reg_unlock(struct wm8350 *wm8350)
{
- u16 key = WM8350_UNLOCK_KEY;
int ret;
+ mutex_lock(&reg_lock_mutex);
+
ldbg(__func__);
- mutex_lock(&io_mutex);
- ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+
+ ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY);
if (ret)
dev_err(wm8350->dev, "unlock failed\n");
- mutex_unlock(&io_mutex);
+
+ wm8350->unlocked = true;
+
+ mutex_unlock(&reg_lock_mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
@@ -395,146 +249,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
}
/*
- * Cache is always host endian.
- */
-static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
-{
- int i, ret = 0;
- u16 value;
- const u16 *reg_map;
-
- switch (type) {
- case 0:
- switch (mode) {
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
- case 0:
- reg_map = wm8350_mode0_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
- case 1:
- reg_map = wm8350_mode1_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
- case 2:
- reg_map = wm8350_mode2_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
- case 3:
- reg_map = wm8350_mode3_defaults;
- break;
-#endif
- default:
- dev_err(wm8350->dev,
- "WM8350 configuration mode %d not supported\n",
- mode);
- return -EINVAL;
- }
- break;
-
- case 1:
- switch (mode) {
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
- case 0:
- reg_map = wm8351_mode0_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
- case 1:
- reg_map = wm8351_mode1_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
- case 2:
- reg_map = wm8351_mode2_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
- case 3:
- reg_map = wm8351_mode3_defaults;
- break;
-#endif
- default:
- dev_err(wm8350->dev,
- "WM8351 configuration mode %d not supported\n",
- mode);
- return -EINVAL;
- }
- break;
-
- case 2:
- switch (mode) {
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
- case 0:
- reg_map = wm8352_mode0_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
- case 1:
- reg_map = wm8352_mode1_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
- case 2:
- reg_map = wm8352_mode2_defaults;
- break;
-#endif
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
- case 3:
- reg_map = wm8352_mode3_defaults;
- break;
-#endif
- default:
- dev_err(wm8350->dev,
- "WM8352 configuration mode %d not supported\n",
- mode);
- return -EINVAL;
- }
- break;
-
- default:
- dev_err(wm8350->dev,
- "WM835x configuration mode %d not supported\n",
- mode);
- return -EINVAL;
- }
-
- wm8350->reg_cache =
- kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
- if (wm8350->reg_cache == NULL)
- return -ENOMEM;
-
- /* Read the initial cache state back from the device - this is
- * a PMIC so the device many not be in a virgin state and we
- * can't rely on the silicon values.
- */
- ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache,
- sizeof(u16) * (WM8350_MAX_REGISTER + 1));
- if (ret < 0) {
- dev_err(wm8350->dev,
- "failed to read initial cache values\n");
- goto out;
- }
-
- /* Mask out uncacheable/unreadable bits and the audio. */
- for (i = 0; i < WM8350_MAX_REGISTER; i++) {
- if (wm8350_reg_io_map[i].readable &&
- (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
- value = be16_to_cpu(wm8350->reg_cache[i]);
- value &= wm8350_reg_io_map[i].readable;
- wm8350->reg_cache[i] = value;
- } else
- wm8350->reg_cache[i] = reg_map[i];
- }
-
-out:
- kfree(wm8350->reg_cache);
- return ret;
-}
-
-/*
* Register a client device. This is non-fatal since there is no need to
* fail the entire device init due to a single platform device failing.
*/
@@ -681,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
goto err;
}
- ret = wm8350_create_cache(wm8350, mask_rev, mode);
- if (ret < 0) {
- dev_err(wm8350->dev, "Failed to create register cache\n");
- return ret;
- }
-
mutex_init(&wm8350->auxadc_mutex);
init_completion(&wm8350->auxadc_done);
ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
- goto err_free;
+ goto err;
if (wm8350->irq_base) {
ret = request_threaded_irq(wm8350->irq_base +
@@ -730,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
err_irq:
wm8350_irq_exit(wm8350);
-err_free:
- kfree(wm8350->reg_cache);
err:
return ret;
}
@@ -758,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350)
free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
wm8350_irq_exit(wm8350);
-
- kfree(wm8350->reg_cache);
}
EXPORT_SYMBOL_GPL(wm8350_device_exit);
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
index a68aceb4e48c..2e57101c8d3d 100644
--- a/drivers/mfd/wm8350-i2c.c
+++ b/drivers/mfd/wm8350-i2c.c
@@ -23,11 +23,6 @@
#include <linux/regmap.h>
#include <linux/slab.h>
-static const struct regmap_config wm8350_regmap = {
- .reg_bits = 8,
- .val_bits = 16,
-};
-
static int wm8350_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c
index 9fd01bf63c51..624ff90501cd 100644
--- a/drivers/mfd/wm8350-irq.c
+++ b/drivers/mfd/wm8350-irq.c
@@ -432,11 +432,9 @@ static void wm8350_irq_sync_unlock(struct irq_data *data)
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
- if (wm8350->irq_masks[i] !=
- wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
- WARN_ON(wm8350_reg_write(wm8350,
- WM8350_INT_STATUS_1_MASK + i,
- wm8350->irq_masks[i]));
+ WARN_ON(regmap_update_bits(wm8350->regmap,
+ WM8350_INT_STATUS_1_MASK + i,
+ 0xffff, wm8350->irq_masks[i]));
}
mutex_unlock(&wm8350->irq_lock);
diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c
index e965139e5cd5..9efc64750fb6 100644
--- a/drivers/mfd/wm8350-regmap.c
+++ b/drivers/mfd/wm8350-regmap.c
@@ -14,3170 +14,18 @@
#include <linux/mfd/wm8350/core.h>
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode0_defaults[] = {
- 0x17FF, /* R0 - Reset/ID */
- 0x1000, /* R1 - ID */
- 0x0000, /* R2 */
- 0x1002, /* R3 - System Control 1 */
- 0x0004, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 - Power Up Interrupt Status */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 - Power Up Interrupt Status Mask */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3B00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - LOUT1 Volume */
- 0x00E4, /* R105 - ROUT1 Volume */
- 0x00E4, /* R106 - LOUT2 Volume */
- 0x02E4, /* R107 - ROUT2 Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 - AIF Test */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x03FC, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0FFC, /* R134 - GPIO Configuration (i/o) */
- 0x0FFC, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0013, /* R140 - GPIO Function Select 1 */
- 0x0000, /* R141 - GPIO Function Select 2 */
- 0x0000, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x002D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0000, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0000, /* R186 - DCDC3 Control */
- 0x0000, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0000, /* R189 - DCDC4 Control */
- 0x0000, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0000, /* R195 - DCDC6 Control */
- 0x0000, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x001B, /* R203 - LDO2 Control */
- 0x0000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001B, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001B, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 */
- 0x4000, /* R220 - RAM BIST 1 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 */
- 0x0000, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 */
- 0x0000, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0000, /* R245 */
- 0x0000, /* R246 */
- 0x0000, /* R247 */
- 0x0000, /* R248 */
- 0x0000, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0000, /* R252 */
- 0x0000, /* R253 */
- 0x0000, /* R254 */
- 0x0000, /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode1_defaults[] = {
- 0x17FF, /* R0 - Reset/ID */
- 0x1000, /* R1 - ID */
- 0x0000, /* R2 */
- 0x1002, /* R3 - System Control 1 */
- 0x0014, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 - Power Up Interrupt Status */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 - Power Up Interrupt Status Mask */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3B00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - LOUT1 Volume */
- 0x00E4, /* R105 - ROUT1 Volume */
- 0x00E4, /* R106 - LOUT2 Volume */
- 0x02E4, /* R107 - ROUT2 Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 - AIF Test */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x03FC, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x00FB, /* R134 - GPIO Configuration (i/o) */
- 0x04FE, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0312, /* R140 - GPIO Function Select 1 */
- 0x1003, /* R141 - GPIO Function Select 2 */
- 0x1331, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x002D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x0062, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0026, /* R186 - DCDC3 Control */
- 0x0400, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0062, /* R189 - DCDC4 Control */
- 0x0400, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0026, /* R195 - DCDC6 Control */
- 0x0800, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x0006, /* R200 - LDO1 Control */
- 0x0400, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0006, /* R203 - LDO2 Control */
- 0x0400, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001B, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001B, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 */
- 0x4000, /* R220 - RAM BIST 1 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 */
- 0x0000, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 */
- 0x0000, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0000, /* R245 */
- 0x0000, /* R246 */
- 0x0000, /* R247 */
- 0x0000, /* R248 */
- 0x0000, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0000, /* R252 */
- 0x0000, /* R253 */
- 0x0000, /* R254 */
- 0x0000, /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode2_defaults[] = {
- 0x17FF, /* R0 - Reset/ID */
- 0x1000, /* R1 - ID */
- 0x0000, /* R2 */
- 0x1002, /* R3 - System Control 1 */
- 0x0014, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 - Power Up Interrupt Status */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 - Power Up Interrupt Status Mask */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3B00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - LOUT1 Volume */
- 0x00E4, /* R105 - ROUT1 Volume */
- 0x00E4, /* R106 - LOUT2 Volume */
- 0x02E4, /* R107 - ROUT2 Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 - AIF Test */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x03FC, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x08FB, /* R134 - GPIO Configuration (i/o) */
- 0x0CFE, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0312, /* R140 - GPIO Function Select 1 */
- 0x0003, /* R141 - GPIO Function Select 2 */
- 0x2331, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x002D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x002E, /* R186 - DCDC3 Control */
- 0x0800, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x000E, /* R189 - DCDC4 Control */
- 0x0800, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0026, /* R195 - DCDC6 Control */
- 0x0C00, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001A, /* R200 - LDO1 Control */
- 0x0800, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0010, /* R203 - LDO2 Control */
- 0x0800, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x000A, /* R206 - LDO3 Control */
- 0x0C00, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001A, /* R209 - LDO4 Control */
- 0x0800, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 */
- 0x4000, /* R220 - RAM BIST 1 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 */
- 0x0000, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 */
- 0x0000, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0000, /* R245 */
- 0x0000, /* R246 */
- 0x0000, /* R247 */
- 0x0000, /* R248 */
- 0x0000, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0000, /* R252 */
- 0x0000, /* R253 */
- 0x0000, /* R254 */
- 0x0000, /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8350_mode3_defaults[] = {
- 0x17FF, /* R0 - Reset/ID */
- 0x1000, /* R1 - ID */
- 0x0000, /* R2 */
- 0x1000, /* R3 - System Control 1 */
- 0x0004, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 - Power Up Interrupt Status */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 - Power Up Interrupt Status Mask */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3B00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - LOUT1 Volume */
- 0x00E4, /* R105 - ROUT1 Volume */
- 0x00E4, /* R106 - LOUT2 Volume */
- 0x02E4, /* R107 - ROUT2 Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 - AIF Test */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x03FC, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0A7B, /* R134 - GPIO Configuration (i/o) */
- 0x06FE, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x1312, /* R140 - GPIO Function Select 1 */
- 0x1030, /* R141 - GPIO Function Select 2 */
- 0x2231, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x002D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x000E, /* R186 - DCDC3 Control */
- 0x0400, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0026, /* R189 - DCDC4 Control */
- 0x0400, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0026, /* R195 - DCDC6 Control */
- 0x0400, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x001C, /* R203 - LDO2 Control */
- 0x0400, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001C, /* R206 - LDO3 Control */
- 0x0400, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001F, /* R209 - LDO4 Control */
- 0x0400, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 */
- 0x4000, /* R220 - RAM BIST 1 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 */
- 0x0000, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 */
- 0x0000, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0000, /* R245 */
- 0x0000, /* R246 */
- 0x0000, /* R247 */
- 0x0000, /* R248 */
- 0x0000, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0000, /* R252 */
- 0x0000, /* R253 */
- 0x0000, /* R254 */
- 0x0000, /* R255 */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode0_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0001, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0004, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0FFC, /* R134 - GPIO Configuration (i/o) */
- 0x0FFC, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0013, /* R140 - GPIO Function Select 1 */
- 0x0000, /* R141 - GPIO Function Select 2 */
- 0x0000, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0000, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0000, /* R186 - DCDC3 Control */
- 0x0000, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0000, /* R189 - DCDC4 Control */
- 0x0000, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x0000, /* R195 */
- 0x0000, /* R196 */
- 0x0006, /* R197 */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x001B, /* R203 - LDO2 Control */
- 0x0000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001B, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001B, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 - FLL Test 1 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x1000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode1_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0001, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0204, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0CFB, /* R134 - GPIO Configuration (i/o) */
- 0x0C1F, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0300, /* R140 - GPIO Function Select 1 */
- 0x1110, /* R141 - GPIO Function Select 2 */
- 0x0013, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0C00, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0026, /* R186 - DCDC3 Control */
- 0x0400, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0062, /* R189 - DCDC4 Control */
- 0x0800, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x000A, /* R195 */
- 0x1000, /* R196 */
- 0x0006, /* R197 */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x0006, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0010, /* R203 - LDO2 Control */
- 0x0C00, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001F, /* R206 - LDO3 Control */
- 0x0800, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x000A, /* R209 - LDO4 Control */
- 0x0800, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 - FLL Test 1 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x1000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x1000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode2_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0001, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0214, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0110, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x09FA, /* R134 - GPIO Configuration (i/o) */
- 0x0DF6, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x1310, /* R140 - GPIO Function Select 1 */
- 0x0003, /* R141 - GPIO Function Select 2 */
- 0x2000, /* R142 - GPIO Function Select 3 */
- 0x0000, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x001A, /* R180 - DCDC1 Control */
- 0x0800, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0056, /* R186 - DCDC3 Control */
- 0x0400, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0026, /* R189 - DCDC4 Control */
- 0x0C00, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x0026, /* R195 */
- 0x0C00, /* R196 */
- 0x0006, /* R197 */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0400, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0010, /* R203 - LDO2 Control */
- 0x0C00, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x0015, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001A, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 - FLL Test 1 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x1000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8351_mode3_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0001, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0204, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0010, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0BFB, /* R134 - GPIO Configuration (i/o) */
- 0x0FFD, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0310, /* R140 - GPIO Function Select 1 */
- 0x0001, /* R141 - GPIO Function Select 2 */
- 0x2300, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0026, /* R186 - DCDC3 Control */
- 0x0800, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0062, /* R189 - DCDC4 Control */
- 0x1400, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x0026, /* R195 */
- 0x0400, /* R196 */
- 0x0006, /* R197 */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x0006, /* R200 - LDO1 Control */
- 0x0C00, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0016, /* R203 - LDO2 Control */
- 0x0000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x0019, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001A, /* R209 - LDO4 Control */
- 0x1000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 - FLL Test 1 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x1000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode0_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0002, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0004, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0FFC, /* R134 - GPIO Configuration (i/o) */
- 0x0FFC, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0013, /* R140 - GPIO Function Select 1 */
- 0x0000, /* R141 - GPIO Function Select 2 */
- 0x0000, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0000, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0000, /* R186 - DCDC3 Control */
- 0x0000, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0000, /* R189 - DCDC4 Control */
- 0x0000, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0000, /* R195 - DCDC6 Control */
- 0x0000, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x001B, /* R203 - LDO2 Control */
- 0x0000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001B, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001B, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x5000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
- 0x5100, /* R252 */
- 0x1000, /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode1_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0002, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0204, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0BFB, /* R134 - GPIO Configuration (i/o) */
- 0x0FFF, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0300, /* R140 - GPIO Function Select 1 */
- 0x0000, /* R141 - GPIO Function Select 2 */
- 0x2300, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x0062, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0006, /* R186 - DCDC3 Control */
- 0x0800, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x0006, /* R189 - DCDC4 Control */
- 0x0C00, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0026, /* R195 - DCDC6 Control */
- 0x1000, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x0002, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x001A, /* R203 - LDO2 Control */
- 0x0000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001F, /* R206 - LDO3 Control */
- 0x0000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001F, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x5000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
- 0x5100, /* R252 */
- 0x1000, /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode2_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0002, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0204, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0000, /* R129 - GPIO Pin pull up Control */
- 0x0110, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x09DA, /* R134 - GPIO Configuration (i/o) */
- 0x0DD6, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x1310, /* R140 - GPIO Function Select 1 */
- 0x0033, /* R141 - GPIO Function Select 2 */
- 0x2000, /* R142 - GPIO Function Select 3 */
- 0x0000, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x000E, /* R180 - DCDC1 Control */
- 0x0800, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0056, /* R186 - DCDC3 Control */
- 0x1800, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x000E, /* R189 - DCDC4 Control */
- 0x1000, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0026, /* R195 - DCDC6 Control */
- 0x0C00, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001C, /* R200 - LDO1 Control */
- 0x0000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0006, /* R203 - LDO2 Control */
- 0x0400, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x001C, /* R206 - LDO3 Control */
- 0x1400, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x001A, /* R209 - LDO4 Control */
- 0x0000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x5000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
- 0x5100, /* R252 */
- 0x1000, /* R253 - DCDC6 Test Controls */
-};
-#endif
-
-#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
-
-#undef WM8350_HAVE_CONFIG_MODE
-#define WM8350_HAVE_CONFIG_MODE
-
-const u16 wm8352_mode3_defaults[] = {
- 0x6143, /* R0 - Reset/ID */
- 0x0000, /* R1 - ID */
- 0x0002, /* R2 - Revision */
- 0x1C02, /* R3 - System Control 1 */
- 0x0204, /* R4 - System Control 2 */
- 0x0000, /* R5 - System Hibernate */
- 0x8A00, /* R6 - Interface Control */
- 0x0000, /* R7 */
- 0x8000, /* R8 - Power mgmt (1) */
- 0x0000, /* R9 - Power mgmt (2) */
- 0x0000, /* R10 - Power mgmt (3) */
- 0x2000, /* R11 - Power mgmt (4) */
- 0x0E00, /* R12 - Power mgmt (5) */
- 0x0000, /* R13 - Power mgmt (6) */
- 0x0000, /* R14 - Power mgmt (7) */
- 0x0000, /* R15 */
- 0x0000, /* R16 - RTC Seconds/Minutes */
- 0x0100, /* R17 - RTC Hours/Day */
- 0x0101, /* R18 - RTC Date/Month */
- 0x1400, /* R19 - RTC Year */
- 0x0000, /* R20 - Alarm Seconds/Minutes */
- 0x0000, /* R21 - Alarm Hours/Day */
- 0x0000, /* R22 - Alarm Date/Month */
- 0x0320, /* R23 - RTC Time Control */
- 0x0000, /* R24 - System Interrupts */
- 0x0000, /* R25 - Interrupt Status 1 */
- 0x0000, /* R26 - Interrupt Status 2 */
- 0x0000, /* R27 */
- 0x0000, /* R28 - Under Voltage Interrupt status */
- 0x0000, /* R29 - Over Current Interrupt status */
- 0x0000, /* R30 - GPIO Interrupt Status */
- 0x0000, /* R31 - Comparator Interrupt Status */
- 0x3FFF, /* R32 - System Interrupts Mask */
- 0x0000, /* R33 - Interrupt Status 1 Mask */
- 0x0000, /* R34 - Interrupt Status 2 Mask */
- 0x0000, /* R35 */
- 0x0000, /* R36 - Under Voltage Interrupt status Mask */
- 0x0000, /* R37 - Over Current Interrupt status Mask */
- 0x0000, /* R38 - GPIO Interrupt Status Mask */
- 0x0000, /* R39 - Comparator Interrupt Status Mask */
- 0x0040, /* R40 - Clock Control 1 */
- 0x0000, /* R41 - Clock Control 2 */
- 0x3A00, /* R42 - FLL Control 1 */
- 0x7086, /* R43 - FLL Control 2 */
- 0xC226, /* R44 - FLL Control 3 */
- 0x0000, /* R45 - FLL Control 4 */
- 0x0000, /* R46 */
- 0x0000, /* R47 */
- 0x0000, /* R48 - DAC Control */
- 0x0000, /* R49 */
- 0x00C0, /* R50 - DAC Digital Volume L */
- 0x00C0, /* R51 - DAC Digital Volume R */
- 0x0000, /* R52 */
- 0x0040, /* R53 - DAC LR Rate */
- 0x0000, /* R54 - DAC Clock Control */
- 0x0000, /* R55 */
- 0x0000, /* R56 */
- 0x0000, /* R57 */
- 0x4000, /* R58 - DAC Mute */
- 0x0000, /* R59 - DAC Mute Volume */
- 0x0000, /* R60 - DAC Side */
- 0x0000, /* R61 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x8000, /* R64 - ADC Control */
- 0x0000, /* R65 */
- 0x00C0, /* R66 - ADC Digital Volume L */
- 0x00C0, /* R67 - ADC Digital Volume R */
- 0x0000, /* R68 - ADC Divider */
- 0x0000, /* R69 */
- 0x0040, /* R70 - ADC LR Rate */
- 0x0000, /* R71 */
- 0x0303, /* R72 - Input Control */
- 0x0000, /* R73 - IN3 Input Control */
- 0x0000, /* R74 - Mic Bias Control */
- 0x0000, /* R75 */
- 0x0000, /* R76 - Output Control */
- 0x0000, /* R77 - Jack Detect */
- 0x0000, /* R78 - Anti Pop Control */
- 0x0000, /* R79 */
- 0x0040, /* R80 - Left Input Volume */
- 0x0040, /* R81 - Right Input Volume */
- 0x0000, /* R82 */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 */
- 0x0800, /* R88 - Left Mixer Control */
- 0x1000, /* R89 - Right Mixer Control */
- 0x0000, /* R90 */
- 0x0000, /* R91 */
- 0x0000, /* R92 - OUT3 Mixer Control */
- 0x0000, /* R93 - OUT4 Mixer Control */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 - Output Left Mixer Volume */
- 0x0000, /* R97 - Output Right Mixer Volume */
- 0x0000, /* R98 - Input Mixer Volume L */
- 0x0000, /* R99 - Input Mixer Volume R */
- 0x0000, /* R100 - Input Mixer Volume */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x00E4, /* R104 - OUT1L Volume */
- 0x00E4, /* R105 - OUT1R Volume */
- 0x00E4, /* R106 - OUT2L Volume */
- 0x02E4, /* R107 - OUT2R Volume */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 - BEEP Volume */
- 0x0A00, /* R112 - AI Formating */
- 0x0000, /* R113 - ADC DAC COMP */
- 0x0020, /* R114 - AI ADC Control */
- 0x0020, /* R115 - AI DAC Control */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x1FFF, /* R128 - GPIO Debounce */
- 0x0010, /* R129 - GPIO Pin pull up Control */
- 0x0000, /* R130 - GPIO Pull down Control */
- 0x0000, /* R131 - GPIO Interrupt Mode */
- 0x0000, /* R132 */
- 0x0000, /* R133 - GPIO Control */
- 0x0BFB, /* R134 - GPIO Configuration (i/o) */
- 0x0FFD, /* R135 - GPIO Pin Polarity / Type */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0310, /* R140 - GPIO Function Select 1 */
- 0x0001, /* R141 - GPIO Function Select 2 */
- 0x2300, /* R142 - GPIO Function Select 3 */
- 0x0003, /* R143 - GPIO Function Select 4 */
- 0x0000, /* R144 - Digitiser Control (1) */
- 0x0002, /* R145 - Digitiser Control (2) */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x7000, /* R152 - AUX1 Readback */
- 0x7000, /* R153 - AUX2 Readback */
- 0x7000, /* R154 - AUX3 Readback */
- 0x7000, /* R155 - AUX4 Readback */
- 0x0000, /* R156 - USB Voltage Readback */
- 0x0000, /* R157 - LINE Voltage Readback */
- 0x0000, /* R158 - BATT Voltage Readback */
- 0x0000, /* R159 - Chip Temp Readback */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 - Generic Comparator Control */
- 0x0000, /* R164 - Generic comparator 1 */
- 0x0000, /* R165 - Generic comparator 2 */
- 0x0000, /* R166 - Generic comparator 3 */
- 0x0000, /* R167 - Generic comparator 4 */
- 0xA00F, /* R168 - Battery Charger Control 1 */
- 0x0B06, /* R169 - Battery Charger Control 2 */
- 0x0000, /* R170 - Battery Charger Control 3 */
- 0x0000, /* R171 */
- 0x0000, /* R172 - Current Sink Driver A */
- 0x0000, /* R173 - CSA Flash control */
- 0x0000, /* R174 - Current Sink Driver B */
- 0x0000, /* R175 - CSB Flash control */
- 0x0000, /* R176 - DCDC/LDO requested */
- 0x032D, /* R177 - DCDC Active options */
- 0x0000, /* R178 - DCDC Sleep options */
- 0x0025, /* R179 - Power-check comparator */
- 0x0006, /* R180 - DCDC1 Control */
- 0x0400, /* R181 - DCDC1 Timeouts */
- 0x1006, /* R182 - DCDC1 Low Power */
- 0x0018, /* R183 - DCDC2 Control */
- 0x0000, /* R184 - DCDC2 Timeouts */
- 0x0000, /* R185 */
- 0x0050, /* R186 - DCDC3 Control */
- 0x0C00, /* R187 - DCDC3 Timeouts */
- 0x0006, /* R188 - DCDC3 Low Power */
- 0x000E, /* R189 - DCDC4 Control */
- 0x0400, /* R190 - DCDC4 Timeouts */
- 0x0006, /* R191 - DCDC4 Low Power */
- 0x0008, /* R192 - DCDC5 Control */
- 0x0000, /* R193 - DCDC5 Timeouts */
- 0x0000, /* R194 */
- 0x0029, /* R195 - DCDC6 Control */
- 0x0800, /* R196 - DCDC6 Timeouts */
- 0x0006, /* R197 - DCDC6 Low Power */
- 0x0000, /* R198 */
- 0x0003, /* R199 - Limit Switch Control */
- 0x001D, /* R200 - LDO1 Control */
- 0x1000, /* R201 - LDO1 Timeouts */
- 0x001C, /* R202 - LDO1 Low Power */
- 0x0017, /* R203 - LDO2 Control */
- 0x1000, /* R204 - LDO2 Timeouts */
- 0x001C, /* R205 - LDO2 Low Power */
- 0x0006, /* R206 - LDO3 Control */
- 0x1000, /* R207 - LDO3 Timeouts */
- 0x001C, /* R208 - LDO3 Low Power */
- 0x0010, /* R209 - LDO4 Control */
- 0x1000, /* R210 - LDO4 Timeouts */
- 0x001C, /* R211 - LDO4 Low Power */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 - VCC_FAULT Masks */
- 0x001F, /* R216 - Main Bandgap Control */
- 0x0000, /* R217 - OSC Control */
- 0x9000, /* R218 - RTC Tick Control */
- 0x0000, /* R219 - Security1 */
- 0x4000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 - Signal overrides */
- 0x0000, /* R225 - DCDC/LDO status */
- 0x0000, /* R226 - Charger Overides/status */
- 0x0000, /* R227 - misc overrides */
- 0x0000, /* R228 - Supply overrides/status 1 */
- 0x0000, /* R229 - Supply overrides/status 2 */
- 0xE000, /* R230 - GPIO Pin Status */
- 0x0000, /* R231 - comparotor overrides */
- 0x0000, /* R232 */
- 0x0000, /* R233 - State Machine status */
- 0x1200, /* R234 */
- 0x0000, /* R235 */
- 0x8000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0003, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0004, /* R243 */
- 0x0300, /* R244 */
- 0x0000, /* R245 */
- 0x0200, /* R246 */
- 0x0000, /* R247 */
- 0x1000, /* R248 - DCDC1 Test Controls */
- 0x5000, /* R249 */
- 0x1000, /* R250 - DCDC3 Test Controls */
- 0x1000, /* R251 - DCDC4 Test Controls */
- 0x5100, /* R252 */
- 0x1000, /* R253 - DCDC6 Test Controls */
-};
-#endif
-
/*
* Access masks.
*/
-const struct wm8350_reg_access wm8350_reg_io_map[] = {
+static const struct wm8350_reg_access {
+ u16 readable; /* Mask of readable bits */
+ u16 writable; /* Mask of writable bits */
+ u16 vol; /* Mask of volatile bits */
+} wm8350_reg_io_map[] = {
/* read write volatile */
- { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */
- { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */
+ { 0xFFFF, 0xFFFF, 0x0000 }, /* R0 - Reset/ID */
+ { 0x7CFF, 0x0C00, 0x0000 }, /* R1 - ID */
{ 0x007F, 0x0000, 0x0000 }, /* R2 - ROM Mask ID */
{ 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */
{ 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */
@@ -3433,3 +281,59 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = {
{ 0x0000, 0x0000, 0x0000 }, /* R254 */
{ 0x0000, 0x0000, 0x0000 }, /* R255 */
};
+
+static bool wm8350_readable(struct device *dev, unsigned int reg)
+{
+ return wm8350_reg_io_map[reg].readable;
+}
+
+static bool wm8350_writeable(struct device *dev, unsigned int reg)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+
+ if (!wm8350->unlocked) {
+ if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
+ reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
+ (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
+ reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
+ return false;
+ }
+
+ return wm8350_reg_io_map[reg].writable;
+}
+
+static bool wm8350_volatile(struct device *dev, unsigned int reg)
+{
+ return wm8350_reg_io_map[reg].vol;
+}
+
+static bool wm8350_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8350_SYSTEM_INTERRUPTS:
+ case WM8350_INT_STATUS_1:
+ case WM8350_INT_STATUS_2:
+ case WM8350_POWER_UP_INT_STATUS:
+ case WM8350_UNDER_VOLTAGE_INT_STATUS:
+ case WM8350_OVER_CURRENT_INT_STATUS:
+ case WM8350_GPIO_INT_STATUS:
+ case WM8350_COMPARATOR_INT_STATUS:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config wm8350_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .max_register = WM8350_MAX_REGISTER,
+ .readable_reg = wm8350_readable,
+ .writeable_reg = wm8350_writeable,
+ .volatile_reg = wm8350_volatile,
+ .precious_reg = wm8350_precious,
+};
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 1e321d349777..eec74aa55fdf 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -283,9 +283,24 @@ static int wm8994_suspend(struct device *dev)
wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
- regcache_cache_only(wm8994->regmap, true);
regcache_mark_dirty(wm8994->regmap);
+ /* Restore GPIO registers to prevent problems with mismatched
+ * pin configurations.
+ */
+ ret = regcache_sync_region(wm8994->regmap, WM8994_GPIO_1,
+ WM8994_GPIO_11);
+ if (ret != 0)
+ dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+
+ /* In case one of the GPIOs is used as a wake input. */
+ ret = regcache_sync_region(wm8994->regmap,
+ WM8994_INTERRUPT_STATUS_1_MASK,
+ WM8994_INTERRUPT_STATUS_1_MASK);
+ if (ret != 0)
+ dev_err(dev, "Failed to restore interrupt mask: %d\n", ret);
+
+ regcache_cache_only(wm8994->regmap, true);
wm8994->suspended = true;
ret = regulator_bulk_disable(wm8994->num_supplies,
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index f1837f669755..0aac4aff17a5 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/registers.h>
#include <linux/delay.h>
@@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = {
int wm8994_irq_init(struct wm8994 *wm8994)
{
int ret;
+ unsigned long irqflags;
+ struct wm8994_pdata *pdata = wm8994->dev->platform_data;
if (!wm8994->irq) {
dev_warn(wm8994->dev,
@@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994)
return 0;
}
+ /* select user or default irq flags */
+ irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+ if (pdata->irq_flags)
+ irqflags = pdata->irq_flags;
+
ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ irqflags,
wm8994->irq_base, &wm8994_irq_chip,
&wm8994->irq_data);
if (ret != 0) {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 154f3ef07631..98a442da892a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -64,6 +64,7 @@ config AB8500_PWM
bool "AB8500 PWM support"
depends on AB8500_CORE && ARCH_U8500
select HAVE_PWM
+ depends on !PWM
help
This driver exports functions to enable/disble/config/free Pulse
Width Modulation in the Analog Baseband Chip AB8500.
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
index 042a8fe4efaa..d7a9aa14e5d5 100644
--- a/drivers/misc/ab8500-pwm.c
+++ b/drivers/misc/ab8500-pwm.c
@@ -142,16 +142,10 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_pwm_match[] = {
- { .compatible = "stericsson,ab8500-pwm", },
- {}
-};
-
static struct platform_driver ab8500_pwm_driver = {
.driver = {
.name = "ab8500-pwm",
.owner = THIS_MODULE,
- .of_match_table = ab8500_pwm_match,
},
.probe = ab8500_pwm_probe,
.remove = __devexit_p(ab8500_pwm_remove),
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
index 28adefe70f96..08aad69c8da4 100644
--- a/drivers/misc/lkdtm.c
+++ b/drivers/misc/lkdtm.c
@@ -477,6 +477,8 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
int i, n, out;
buf = (char *)__get_free_page(GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
for (i = 0; i < ARRAY_SIZE(cp_type); i++)
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index 2b62232c2c6a..acfaeeb9e01a 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -349,6 +349,11 @@ void st_int_recv(void *disc_data,
st_gdata->rx_skb = alloc_skb(
st_gdata->list[type]->max_frame_size,
GFP_ATOMIC);
+ if (st_gdata->rx_skb == NULL) {
+ pr_err("out of memory: dropping\n");
+ goto done;
+ }
+
skb_reserve(st_gdata->rx_skb,
st_gdata->list[type]->reserve);
/* next 2 required for BT only */
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 3e8dcf8d2e05..50e08f03aa65 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -17,10 +17,12 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
+#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/clk.h>
@@ -128,6 +130,10 @@ struct mmc_omap_host {
unsigned char id; /* 16xx chips have 2 MMC blocks */
struct clk * iclk;
struct clk * fclk;
+ struct dma_chan *dma_rx;
+ u32 dma_rx_burst;
+ struct dma_chan *dma_tx;
+ u32 dma_tx_burst;
struct resource *mem_res;
void __iomem *virt_base;
unsigned int phys_base;
@@ -153,12 +159,8 @@ struct mmc_omap_host {
unsigned use_dma:1;
unsigned brs_received:1, dma_done:1;
- unsigned dma_is_read:1;
unsigned dma_in_use:1;
- int dma_ch;
spinlock_t dma_lock;
- struct timer_list dma_timer;
- unsigned dma_len;
struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS];
struct mmc_omap_slot *current_slot;
@@ -406,18 +408,25 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
int abort)
{
enum dma_data_direction dma_data_dir;
+ struct device *dev = mmc_dev(host->mmc);
+ struct dma_chan *c;
- BUG_ON(host->dma_ch < 0);
- if (data->error)
- omap_stop_dma(host->dma_ch);
- /* Release DMA channel lazily */
- mod_timer(&host->dma_timer, jiffies + HZ);
- if (data->flags & MMC_DATA_WRITE)
+ if (data->flags & MMC_DATA_WRITE) {
dma_data_dir = DMA_TO_DEVICE;
- else
+ c = host->dma_tx;
+ } else {
dma_data_dir = DMA_FROM_DEVICE;
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
- dma_data_dir);
+ c = host->dma_rx;
+ }
+ if (c) {
+ if (data->error) {
+ dmaengine_terminate_all(c);
+ /* Claim nothing transferred on error... */
+ data->bytes_xfered = 0;
+ }
+ dev = c->device->dev;
+ }
+ dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
}
static void mmc_omap_send_stop_work(struct work_struct *work)
@@ -525,16 +534,6 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
}
static void
-mmc_omap_dma_timer(unsigned long data)
-{
- struct mmc_omap_host *host = (struct mmc_omap_host *) data;
-
- BUG_ON(host->dma_ch < 0);
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
-}
-
-static void
mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
{
unsigned long flags;
@@ -891,159 +890,15 @@ static void mmc_omap_cover_handler(unsigned long param)
jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
}
-/* Prepare to transfer the next segment of a scatterlist */
-static void
-mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
+static void mmc_omap_dma_callback(void *priv)
{
- int dma_ch = host->dma_ch;
- unsigned long data_addr;
- u16 buf, frame;
- u32 count;
- struct scatterlist *sg = &data->sg[host->sg_idx];
- int src_port = 0;
- int dst_port = 0;
- int sync_dev = 0;
-
- data_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
- frame = data->blksz;
- count = sg_dma_len(sg);
-
- if ((data->blocks == 1) && (count > data->blksz))
- count = frame;
-
- host->dma_len = count;
-
- /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
- * Use 16 or 32 word frames when the blocksize is at least that large.
- * Blocksize is usually 512 bytes; but not for some SD reads.
- */
- if (cpu_is_omap15xx() && frame > 32)
- frame = 32;
- else if (frame > 64)
- frame = 64;
- count /= frame;
- frame >>= 1;
-
- if (!(data->flags & MMC_DATA_WRITE)) {
- buf = 0x800f | ((frame - 1) << 8);
-
- if (cpu_class_is_omap1()) {
- src_port = OMAP_DMA_PORT_TIPB;
- dst_port = OMAP_DMA_PORT_EMIFF;
- }
- if (cpu_is_omap24xx())
- sync_dev = OMAP24XX_DMA_MMC1_RX;
-
- omap_set_dma_src_params(dma_ch, src_port,
- OMAP_DMA_AMODE_CONSTANT,
- data_addr, 0, 0);
- omap_set_dma_dest_params(dma_ch, dst_port,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sg), 0, 0);
- omap_set_dma_dest_data_pack(dma_ch, 1);
- omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
- } else {
- buf = 0x0f80 | ((frame - 1) << 0);
-
- if (cpu_class_is_omap1()) {
- src_port = OMAP_DMA_PORT_EMIFF;
- dst_port = OMAP_DMA_PORT_TIPB;
- }
- if (cpu_is_omap24xx())
- sync_dev = OMAP24XX_DMA_MMC1_TX;
-
- omap_set_dma_dest_params(dma_ch, dst_port,
- OMAP_DMA_AMODE_CONSTANT,
- data_addr, 0, 0);
- omap_set_dma_src_params(dma_ch, src_port,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sg), 0, 0);
- omap_set_dma_src_data_pack(dma_ch, 1);
- omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
- }
+ struct mmc_omap_host *host = priv;
+ struct mmc_data *data = host->data;
- /* Max limit for DMA frame count is 0xffff */
- BUG_ON(count > 0xffff);
+ /* If we got to the end of DMA, assume everything went well */
+ data->bytes_xfered += data->blocks * data->blksz;
- OMAP_MMC_WRITE(host, BUF, buf);
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
- frame, count, OMAP_DMA_SYNC_FRAME,
- sync_dev, 0);
-}
-
-/* A scatterlist segment completed */
-static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
-{
- struct mmc_omap_host *host = (struct mmc_omap_host *) data;
- struct mmc_data *mmcdat = host->data;
-
- if (unlikely(host->dma_ch < 0)) {
- dev_err(mmc_dev(host->mmc),
- "DMA callback while DMA not enabled\n");
- return;
- }
- /* FIXME: We really should do something to _handle_ the errors */
- if (ch_status & OMAP1_DMA_TOUT_IRQ) {
- dev_err(mmc_dev(host->mmc),"DMA timeout\n");
- return;
- }
- if (ch_status & OMAP_DMA_DROP_IRQ) {
- dev_err(mmc_dev(host->mmc), "DMA sync error\n");
- return;
- }
- if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
- return;
- }
- mmcdat->bytes_xfered += host->dma_len;
- host->sg_idx++;
- if (host->sg_idx < host->sg_len) {
- mmc_omap_prepare_dma(host, host->data);
- omap_start_dma(host->dma_ch);
- } else
- mmc_omap_dma_done(host, host->data);
-}
-
-static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
-{
- const char *dma_dev_name;
- int sync_dev, dma_ch, is_read, r;
-
- is_read = !(data->flags & MMC_DATA_WRITE);
- del_timer_sync(&host->dma_timer);
- if (host->dma_ch >= 0) {
- if (is_read == host->dma_is_read)
- return 0;
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
- }
-
- if (is_read) {
- if (host->id == 0) {
- sync_dev = OMAP_DMA_MMC_RX;
- dma_dev_name = "MMC1 read";
- } else {
- sync_dev = OMAP_DMA_MMC2_RX;
- dma_dev_name = "MMC2 read";
- }
- } else {
- if (host->id == 0) {
- sync_dev = OMAP_DMA_MMC_TX;
- dma_dev_name = "MMC1 write";
- } else {
- sync_dev = OMAP_DMA_MMC2_TX;
- dma_dev_name = "MMC2 write";
- }
- }
- r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb,
- host, &dma_ch);
- if (r != 0) {
- dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
- return r;
- }
- host->dma_ch = dma_ch;
- host->dma_is_read = is_read;
-
- return 0;
+ mmc_omap_dma_done(host, data);
}
static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
@@ -1118,33 +973,85 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
host->sg_idx = 0;
if (use_dma) {
- if (mmc_omap_get_dma_channel(host, data) == 0) {
- enum dma_data_direction dma_data_dir;
-
- if (data->flags & MMC_DATA_WRITE)
- dma_data_dir = DMA_TO_DEVICE;
- else
- dma_data_dir = DMA_FROM_DEVICE;
-
- host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- sg_len, dma_data_dir);
- host->total_bytes_left = 0;
- mmc_omap_prepare_dma(host, req->data);
- host->brs_received = 0;
- host->dma_done = 0;
- host->dma_in_use = 1;
- } else
- use_dma = 0;
+ enum dma_data_direction dma_data_dir;
+ struct dma_async_tx_descriptor *tx;
+ struct dma_chan *c;
+ u32 burst, *bp;
+ u16 buf;
+
+ /*
+ * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
+ * and 24xx. Use 16 or 32 word frames when the
+ * blocksize is at least that large. Blocksize is
+ * usually 512 bytes; but not for some SD reads.
+ */
+ burst = cpu_is_omap15xx() ? 32 : 64;
+ if (burst > data->blksz)
+ burst = data->blksz;
+
+ burst >>= 1;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ c = host->dma_tx;
+ bp = &host->dma_tx_burst;
+ buf = 0x0f80 | (burst - 1) << 0;
+ dma_data_dir = DMA_TO_DEVICE;
+ } else {
+ c = host->dma_rx;
+ bp = &host->dma_rx_burst;
+ buf = 0x800f | (burst - 1) << 8;
+ dma_data_dir = DMA_FROM_DEVICE;
+ }
+
+ if (!c)
+ goto use_pio;
+
+ /* Only reconfigure if we have a different burst size */
+ if (*bp != burst) {
+ struct dma_slave_config cfg;
+
+ cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+ cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.src_maxburst = burst;
+ cfg.dst_maxburst = burst;
+
+ if (dmaengine_slave_config(c, &cfg))
+ goto use_pio;
+
+ *bp = burst;
+ }
+
+ host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
+ dma_data_dir);
+ if (host->sg_len == 0)
+ goto use_pio;
+
+ tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
+ data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx)
+ goto use_pio;
+
+ OMAP_MMC_WRITE(host, BUF, buf);
+
+ tx->callback = mmc_omap_dma_callback;
+ tx->callback_param = host;
+ dmaengine_submit(tx);
+ host->brs_received = 0;
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+ return;
}
+ use_pio:
/* Revert to PIO? */
- if (!use_dma) {
- OMAP_MMC_WRITE(host, BUF, 0x1f1f);
- host->total_bytes_left = data->blocks * block_size;
- host->sg_len = sg_len;
- mmc_omap_sg_to_buf(host);
- host->dma_in_use = 0;
- }
+ OMAP_MMC_WRITE(host, BUF, 0x1f1f);
+ host->total_bytes_left = data->blocks * block_size;
+ host->sg_len = sg_len;
+ mmc_omap_sg_to_buf(host);
+ host->dma_in_use = 0;
}
static void mmc_omap_start_request(struct mmc_omap_host *host,
@@ -1157,8 +1064,12 @@ static void mmc_omap_start_request(struct mmc_omap_host *host,
/* only touch fifo AFTER the controller readies it */
mmc_omap_prepare_data(host, req);
mmc_omap_start_command(host, req->cmd);
- if (host->dma_in_use)
- omap_start_dma(host->dma_ch);
+ if (host->dma_in_use) {
+ struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
+ host->dma_tx : host->dma_rx;
+
+ dma_async_issue_pending(c);
+ }
}
static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
@@ -1400,6 +1311,8 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_omap_host *host = NULL;
struct resource *res;
+ dma_cap_mask_t mask;
+ unsigned sig;
int i, ret = 0;
int irq;
@@ -1439,7 +1352,6 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
spin_lock_init(&host->dma_lock);
- setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
spin_lock_init(&host->slot_lock);
init_waitqueue_head(&host->slot_wq);
@@ -1450,11 +1362,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
host->id = pdev->id;
host->mem_res = res;
host->irq = irq;
-
host->use_dma = 1;
- host->dev->dma_mask = &pdata->dma_mask;
- host->dma_ch = -1;
-
host->irq = irq;
host->phys_base = host->mem_res->start;
host->virt_base = ioremap(res->start, resource_size(res));
@@ -1474,9 +1382,48 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
goto err_free_iclk;
}
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ host->dma_tx_burst = -1;
+ host->dma_rx_burst = -1;
+
+ if (cpu_is_omap24xx())
+ sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
+ else
+ sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+ host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+ if (!host->dma_tx) {
+ dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+ sig);
+ goto err_dma;
+ }
+#else
+ if (!host->dma_tx)
+ dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
+ sig);
+#endif
+ if (cpu_is_omap24xx())
+ sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
+ else
+ sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+ host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+ if (!host->dma_rx) {
+ dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
+ sig);
+ goto err_dma;
+ }
+#else
+ if (!host->dma_rx)
+ dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
+ sig);
+#endif
+
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
if (ret)
- goto err_free_fclk;
+ goto err_free_dma;
if (pdata->init != NULL) {
ret = pdata->init(&pdev->dev);
@@ -1510,7 +1457,11 @@ err_plat_cleanup:
pdata->cleanup(&pdev->dev);
err_free_irq:
free_irq(host->irq, host);
-err_free_fclk:
+err_free_dma:
+ if (host->dma_tx)
+ dma_release_channel(host->dma_tx);
+ if (host->dma_rx)
+ dma_release_channel(host->dma_rx);
clk_put(host->fclk);
err_free_iclk:
clk_disable(host->iclk);
@@ -1545,6 +1496,11 @@ static int __devexit mmc_omap_remove(struct platform_device *pdev)
clk_disable(host->iclk);
clk_put(host->iclk);
+ if (host->dma_tx)
+ dma_release_channel(host->dma_tx);
+ if (host->dma_rx)
+ dma_release_channel(host->dma_rx);
+
iounmap(host->virt_base);
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index bc28627af66b..3a09f93cc3b6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
+#include <linux/dmaengine.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -29,6 +30,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
@@ -37,7 +39,6 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
-#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
#include <plat/mmc.h>
@@ -166,7 +167,8 @@ struct omap_hsmmc_host {
int suspended;
int irq;
int use_dma, dma_ch;
- int dma_line_tx, dma_line_rx;
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
int slot_id;
int response_busy;
int context_loss;
@@ -797,6 +799,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
return DMA_FROM_DEVICE;
}
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+ struct mmc_data *data)
+{
+ return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
{
int dma_ch;
@@ -889,10 +897,13 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
spin_unlock_irqrestore(&host->irq_lock, flags);
if (host->use_dma && dma_ch != -1) {
- dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
- host->data->sg_len,
+ struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+ dmaengine_terminate_all(chan);
+ dma_unmap_sg(chan->device->dev,
+ host->data->sg, host->data->sg_len,
omap_hsmmc_get_dma_dir(host, host->data));
- omap_free_dma(dma_ch);
+
host->data->host_cookie = 0;
}
host->data = NULL;
@@ -1190,90 +1201,29 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
- struct mmc_data *data)
-{
- int sync_dev;
-
- if (data->flags & MMC_DATA_WRITE)
- sync_dev = host->dma_line_tx;
- else
- sync_dev = host->dma_line_rx;
- return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
- struct mmc_data *data,
- struct scatterlist *sgl)
-{
- int blksz, nblk, dma_ch;
-
- dma_ch = host->dma_ch;
- if (data->flags & MMC_DATA_WRITE) {
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- } else {
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- }
-
- blksz = host->data->blksz;
- nblk = sg_dma_len(sgl) / blksz;
-
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
- blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
- omap_hsmmc_get_dma_sync_dev(host, data),
- !(data->flags & MMC_DATA_WRITE));
-
- omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
+static void omap_hsmmc_dma_callback(void *param)
{
- struct omap_hsmmc_host *host = cb_data;
+ struct omap_hsmmc_host *host = param;
+ struct dma_chan *chan;
struct mmc_data *data;
- int dma_ch, req_in_progress;
- unsigned long flags;
-
- if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
- dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
- ch_status);
- return;
- }
+ int req_in_progress;
- spin_lock_irqsave(&host->irq_lock, flags);
+ spin_lock_irq(&host->irq_lock);
if (host->dma_ch < 0) {
- spin_unlock_irqrestore(&host->irq_lock, flags);
+ spin_unlock_irq(&host->irq_lock);
return;
}
data = host->mrq->data;
- host->dma_sg_idx++;
- if (host->dma_sg_idx < host->dma_len) {
- /* Fire up the next transfer. */
- omap_hsmmc_config_dma_params(host, data,
- data->sg + host->dma_sg_idx);
- spin_unlock_irqrestore(&host->irq_lock, flags);
- return;
- }
-
+ chan = omap_hsmmc_get_dma_chan(host, data);
if (!data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ dma_unmap_sg(chan->device->dev,
+ data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
req_in_progress = host->req_in_progress;
- dma_ch = host->dma_ch;
host->dma_ch = -1;
- spin_unlock_irqrestore(&host->irq_lock, flags);
-
- omap_free_dma(dma_ch);
+ spin_unlock_irq(&host->irq_lock);
/* If DMA has finished after TC, complete the request */
if (!req_in_progress) {
@@ -1286,7 +1236,8 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_data *data,
- struct omap_hsmmc_next *next)
+ struct omap_hsmmc_next *next,
+ struct dma_chan *chan)
{
int dma_len;
@@ -1301,8 +1252,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
/* Check if next job is already prepared */
if (next ||
(!next && data->host_cookie != host->next_data.cookie)) {
- dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
+ dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
} else {
@@ -1329,8 +1279,11 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
- int dma_ch = 0, ret = 0, i;
+ struct dma_slave_config cfg;
+ struct dma_async_tx_descriptor *tx;
+ int ret = 0, i;
struct mmc_data *data = req->data;
+ struct dma_chan *chan;
/* Sanity check: all the SG entries must be aligned by block size. */
for (i = 0; i < data->sg_len; i++) {
@@ -1348,22 +1301,41 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
BUG_ON(host->dma_ch != -1);
- ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
- "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
- if (ret != 0) {
- dev_err(mmc_dev(host->mmc),
- "%s: omap_request_dma() failed with %d\n",
- mmc_hostname(host->mmc), ret);
+ chan = omap_hsmmc_get_dma_chan(host, data);
+
+ cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = data->blksz / 4;
+ cfg.dst_maxburst = data->blksz / 4;
+
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret)
return ret;
- }
- ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
+
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
if (ret)
return ret;
- host->dma_ch = dma_ch;
- host->dma_sg_idx = 0;
+ tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+ data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx) {
+ dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+ /* FIXME: cleanup */
+ return -1;
+ }
+
+ tx->callback = omap_hsmmc_dma_callback;
+ tx->callback_param = host;
- omap_hsmmc_config_dma_params(host, data, data->sg);
+ /* Does not fail */
+ dmaengine_submit(tx);
+
+ host->dma_ch = 1;
+
+ dma_async_issue_pending(chan);
return 0;
}
@@ -1445,11 +1417,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- if (host->use_dma) {
- if (data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
- omap_hsmmc_get_dma_dir(host, data));
+ if (host->use_dma && data->host_cookie) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+
+ dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
data->host_cookie = 0;
}
}
@@ -1464,10 +1436,13 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
return ;
}
- if (host->use_dma)
+ if (host->use_dma) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+
if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
- &host->next_data))
+ &host->next_data, c))
mrq->data->host_cookie = 0;
+ }
}
/*
@@ -1800,6 +1775,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
struct resource *res;
int ret, irq;
const struct of_device_id *match;
+ dma_cap_mask_t mask;
+ unsigned tx_req, rx_req;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -1844,7 +1821,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
host->pdata = pdata;
host->dev = &pdev->dev;
host->use_dma = 1;
- host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
host->slot_id = 0;
@@ -1934,7 +1910,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = -ENXIO;
goto err_irq;
}
- host->dma_line_tx = res->start;
+ tx_req = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
if (!res) {
@@ -1942,7 +1918,24 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = -ENXIO;
goto err_irq;
}
- host->dma_line_rx = res->start;
+ rx_req = res->start;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+ if (!host->rx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+ ret = -ENXIO;
+ goto err_irq;
+ }
+
+ host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+ if (!host->tx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+ ret = -ENXIO;
+ goto err_irq;
+ }
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
@@ -2021,6 +2014,10 @@ err_reg:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
@@ -2056,6 +2053,11 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
+
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index a6fa884ae49b..100b6775e175 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -52,9 +52,10 @@
#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1)
#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1)
+#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa
-#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
#define JZ_NAND_MEM_CMD_OFFSET 0x08000
+#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
struct jz_nand {
struct mtd_info mtd;
@@ -62,8 +63,11 @@ struct jz_nand {
void __iomem *base;
struct resource *mem;
- void __iomem *bank_base;
- struct resource *bank_mem;
+ unsigned char banks[JZ_NAND_NUM_BANKS];
+ void __iomem *bank_base[JZ_NAND_NUM_BANKS];
+ struct resource *bank_mem[JZ_NAND_NUM_BANKS];
+
+ int selected_bank;
struct jz_nand_platform_data *pdata;
bool is_reading;
@@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
return container_of(mtd, struct jz_nand, mtd);
}
+static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ struct nand_chip *chip = mtd->priv;
+ uint32_t ctrl;
+ int banknr;
+
+ ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+ ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK;
+
+ if (chipnr == -1) {
+ banknr = -1;
+ } else {
+ banknr = nand->banks[chipnr] - 1;
+ chip->IO_ADDR_R = nand->bank_base[banknr];
+ chip->IO_ADDR_W = nand->bank_base[banknr];
+ }
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ nand->selected_bank = banknr;
+}
+
static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
struct nand_chip *chip = mtd->priv;
uint32_t reg;
+ void __iomem *bank_base = nand->bank_base[nand->selected_bank];
+
+ BUG_ON(nand->selected_bank < 0);
if (ctrl & NAND_CTRL_CHANGE) {
BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE));
if (ctrl & NAND_ALE)
- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET;
+ bank_base += JZ_NAND_MEM_ADDR_OFFSET;
else if (ctrl & NAND_CLE)
- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET;
- else
- chip->IO_ADDR_W = nand->bank_base;
+ bank_base += JZ_NAND_MEM_CMD_OFFSET;
+ chip->IO_ADDR_W = bank_base;
reg = readl(nand->base + JZ_REG_NAND_CTRL);
if (ctrl & NAND_NCE)
- reg |= JZ_NAND_CTRL_ASSERT_CHIP(0);
+ reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
else
- reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
+ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
writel(reg, nand->base + JZ_REG_NAND_CTRL);
}
if (dat != NAND_CMD_NONE)
@@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
}
static int jz_nand_ioremap_resource(struct platform_device *pdev,
- const char *name, struct resource **res, void __iomem **base)
+ const char *name, struct resource **res, void *__iomem *base)
{
int ret;
@@ -288,6 +316,90 @@ err:
return ret;
}
+static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base)
+{
+ iounmap(base);
+ release_mem_region(res->start, resource_size(res));
+}
+
+static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) {
+ int ret;
+ int gpio;
+ char gpio_name[9];
+ char res_name[6];
+ uint32_t ctrl;
+ struct mtd_info *mtd = &nand->mtd;
+ struct nand_chip *chip = &nand->chip;
+
+ /* Request GPIO port. */
+ gpio = JZ_GPIO_MEM_CS0 + bank - 1;
+ sprintf(gpio_name, "NAND CS%d", bank);
+ ret = gpio_request(gpio, gpio_name);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request %s gpio %d: %d\n",
+ gpio_name, gpio, ret);
+ goto notfound_gpio;
+ }
+
+ /* Request I/O resource. */
+ sprintf(res_name, "bank%d", bank);
+ ret = jz_nand_ioremap_resource(pdev, res_name,
+ &nand->bank_mem[bank - 1],
+ &nand->bank_base[bank - 1]);
+ if (ret)
+ goto notfound_resource;
+
+ /* Enable chip in bank. */
+ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0);
+ ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+ ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1);
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ if (chipnr == 0) {
+ /* Detect first chip. */
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ goto notfound_id;
+
+ /* Retrieve the IDs from the first chip. */
+ chip->select_chip(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ *nand_maf_id = chip->read_byte(mtd);
+ *nand_dev_id = chip->read_byte(mtd);
+ } else {
+ /* Detect additional chip. */
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ if (*nand_maf_id != chip->read_byte(mtd)
+ || *nand_dev_id != chip->read_byte(mtd)) {
+ ret = -ENODEV;
+ goto notfound_id;
+ }
+
+ /* Update size of the MTD. */
+ chip->numchips++;
+ mtd->size += chip->chipsize;
+ }
+
+ dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank);
+ return 0;
+
+notfound_id:
+ dev_info(&pdev->dev, "No chip found on bank %i\n", bank);
+ ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1));
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE);
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+notfound_resource:
+ gpio_free(gpio);
+notfound_gpio:
+ return ret;
+}
+
static int __devinit jz_nand_probe(struct platform_device *pdev)
{
int ret;
@@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
struct nand_chip *chip;
struct mtd_info *mtd;
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ size_t chipnr, bank_idx;
+ uint8_t nand_maf_id = 0, nand_dev_id = 0;
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
@@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base);
if (ret)
goto err_free;
- ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem,
- &nand->bank_base);
- if (ret)
- goto err_iounmap_mmio;
if (pdata && gpio_is_valid(pdata->busy_gpio)) {
ret = gpio_request(pdata->busy_gpio, "NAND busy pin");
@@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Failed to request busy gpio %d: %d\n",
pdata->busy_gpio, ret);
- goto err_iounmap_mem;
+ goto err_iounmap_mmio;
}
}
@@ -339,22 +449,51 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
chip->chip_delay = 50;
chip->cmd_ctrl = jz_nand_cmd_ctrl;
+ chip->select_chip = jz_nand_select_chip;
if (pdata && gpio_is_valid(pdata->busy_gpio))
chip->dev_ready = jz_nand_dev_ready;
- chip->IO_ADDR_R = nand->bank_base;
- chip->IO_ADDR_W = nand->bank_base;
-
nand->pdata = pdata;
platform_set_drvdata(pdev, nand);
- writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL);
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret) {
- dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ /* We are going to autodetect NAND chips in the banks specified in the
+ * platform data. Although nand_scan_ident() can detect multiple chips,
+ * it requires those chips to be numbered consecuitively, which is not
+ * always the case for external memory banks. And a fixed chip-to-bank
+ * mapping is not practical either, since for example Dingoo units
+ * produced at different times have NAND chips in different banks.
+ */
+ chipnr = 0;
+ for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) {
+ unsigned char bank;
+
+ /* If there is no platform data, look for NAND in bank 1,
+ * which is the most likely bank since it is the only one
+ * that can be booted from.
+ */
+ bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1;
+ if (bank == 0)
+ break;
+ if (bank > JZ_NAND_NUM_BANKS) {
+ dev_warn(&pdev->dev,
+ "Skipping non-existing bank: %d\n", bank);
+ continue;
+ }
+ /* The detection routine will directly or indirectly call
+ * jz_nand_select_chip(), so nand->banks has to contain the
+ * bank we're checking.
+ */
+ nand->banks[chipnr] = bank;
+ if (jz_nand_detect_bank(pdev, nand, bank, chipnr,
+ &nand_maf_id, &nand_dev_id) == 0)
+ chipnr++;
+ else
+ nand->banks[chipnr] = 0;
+ }
+ if (chipnr == 0) {
+ dev_err(&pdev->dev, "No NAND chips found\n");
+ goto err_gpio_busy;
}
if (pdata && pdata->ident_callback) {
@@ -364,8 +503,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
ret = nand_scan_tail(mtd);
if (ret) {
- dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ dev_err(&pdev->dev, "Failed to scan NAND\n");
+ goto err_unclaim_banks;
}
ret = mtd_device_parse_register(mtd, NULL, NULL,
@@ -382,14 +521,21 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
return 0;
err_nand_release:
- nand_release(&nand->mtd);
-err_gpio_free:
+ nand_release(mtd);
+err_unclaim_banks:
+ while (chipnr--) {
+ unsigned char bank = nand->banks[chipnr];
+ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+ }
+ writel(0, nand->base + JZ_REG_NAND_CTRL);
+err_gpio_busy:
+ if (pdata && gpio_is_valid(pdata->busy_gpio))
+ gpio_free(pdata->busy_gpio);
platform_set_drvdata(pdev, NULL);
- gpio_free(pdata->busy_gpio);
-err_iounmap_mem:
- iounmap(nand->bank_base);
err_iounmap_mmio:
- iounmap(nand->base);
+ jz_nand_iounmap_resource(nand->mem, nand->base);
err_free:
kfree(nand);
return ret;
@@ -398,16 +544,26 @@ err_free:
static int __devexit jz_nand_remove(struct platform_device *pdev)
{
struct jz_nand *nand = platform_get_drvdata(pdev);
+ struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ size_t i;
nand_release(&nand->mtd);
/* Deassert and disable all chips */
writel(0, nand->base + JZ_REG_NAND_CTRL);
- iounmap(nand->bank_base);
- release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem));
- iounmap(nand->base);
- release_mem_region(nand->mem->start, resource_size(nand->mem));
+ for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) {
+ unsigned char bank = nand->banks[i];
+ if (bank != 0) {
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+ }
+ }
+ if (pdata && gpio_is_valid(pdata->busy_gpio))
+ gpio_free(pdata->busy_gpio);
+
+ jz_nand_iounmap_resource(nand->mem, nand->base);
platform_set_drvdata(pdev, NULL);
kfree(nand);
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index d7f681d0c9b9..e9309b3659e7 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -9,6 +9,7 @@
*/
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -18,6 +19,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/omap-dma.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -123,7 +125,7 @@ struct omap_nand_info {
int gpmc_cs;
unsigned long phys_base;
struct completion comp;
- int dma_ch;
+ struct dma_chan *dma;
int gpmc_irq;
enum {
OMAP_NAND_IO_READ = 0, /* read */
@@ -336,12 +338,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
}
/*
- * omap_nand_dma_cb: callback on the completion of dma transfer
- * @lch: logical channel
- * @ch_satuts: channel status
+ * omap_nand_dma_callback: callback on the completion of dma transfer
* @data: pointer to completion data structure
*/
-static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_nand_dma_callback(void *data)
{
complete((struct completion *) data);
}
@@ -358,17 +358,13 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
+ struct dma_async_tx_descriptor *tx;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
- dma_addr_t dma_addr;
- int ret;
+ struct scatterlist sg;
unsigned long tim, limit;
-
- /* The fifo depth is 64 bytes max.
- * But configure the FIFO-threahold to 32 to get a sync at each frame
- * and frame length is 32 bytes.
- */
- int buf_len = len >> 6;
+ unsigned n;
+ int ret;
if (addr >= high_memory) {
struct page *p1;
@@ -382,40 +378,33 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
}
- dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
- if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
+ sg_init_one(&sg, addr, len);
+ n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
+ if (n == 0) {
dev_err(&info->pdev->dev,
"Couldn't DMA map a %d byte buffer\n", len);
goto out_copy;
}
- if (is_write) {
- omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- info->phys_base, 0, 0);
- omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dma_addr, 0, 0);
- omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
- OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
- } else {
- omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- info->phys_base, 0, 0);
- omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dma_addr, 0, 0);
- omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
- OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
- }
- /* configure and start prefetch transfer */
+ tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
+ is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx)
+ goto out_copy_unmap;
+
+ tx->callback = omap_nand_dma_callback;
+ tx->callback_param = &info->comp;
+ dmaengine_submit(tx);
+
+ /* configure and start prefetch transfer */
ret = gpmc_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
if (ret)
/* PFPW engine is busy, use cpu copy method */
goto out_copy_unmap;
init_completion(&info->comp);
-
- omap_start_dma(info->dma_ch);
+ dma_async_issue_pending(info->dma);
/* setup and start DMA using dma_addr */
wait_for_completion(&info->comp);
@@ -427,11 +416,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
/* disable and stop the PFPW engine */
gpmc_prefetch_reset(info->gpmc_cs);
- dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
return 0;
out_copy_unmap:
- dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
out_copy:
if (info->nand.options & NAND_BUSWIDTH_16)
is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
@@ -1164,6 +1153,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
struct omap_nand_platform_data *pdata;
int err;
int i, offset;
+ dma_cap_mask_t mask;
+ unsigned sig;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
@@ -1244,18 +1235,31 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
break;
case NAND_OMAP_PREFETCH_DMA:
- err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
- omap_nand_dma_cb, &info->comp, &info->dma_ch);
- if (err < 0) {
- info->dma_ch = -1;
- dev_err(&pdev->dev, "DMA request failed!\n");
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ sig = OMAP24XX_DMA_GPMC;
+ info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!info->dma) {
+ dev_err(&pdev->dev, "DMA engine request failed\n");
+ err = -ENXIO;
goto out_release_mem_region;
} else {
- omap_set_dma_dest_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
- omap_set_dma_src_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
-
+ struct dma_slave_config cfg;
+ int rc;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = info->phys_base;
+ cfg.dst_addr = info->phys_base;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = 16;
+ cfg.dst_maxburst = 16;
+ rc = dmaengine_slave_config(info->dma, &cfg);
+ if (rc) {
+ dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
+ rc);
+ goto out_release_mem_region;
+ }
info->nand.read_buf = omap_read_buf_dma_pref;
info->nand.write_buf = omap_write_buf_dma_pref;
}
@@ -1358,6 +1362,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
return 0;
out_release_mem_region:
+ if (info->dma)
+ dma_release_channel(info->dma);
release_mem_region(info->phys_base, NAND_IO_SIZE);
out_free_info:
kfree(info);
@@ -1373,8 +1379,8 @@ static int omap_nand_remove(struct platform_device *pdev)
omap3_free_bch(&info->mtd);
platform_set_drvdata(pdev, NULL);
- if (info->dma_ch != -1)
- omap_free_dma(info->dma_ch);
+ if (info->dma)
+ dma_release_channel(info->dma);
if (info->gpmc_irq)
free_irq(info->gpmc_irq, info);
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index f0921d16f0a9..6ff7ad006c30 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -74,7 +74,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
const struct platform_device_id *id;
struct resource *mem;
int irq;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
/* get the appropriate clk */
@@ -84,7 +83,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
ret = -ENODEV;
goto exit;
}
-#endif
/* get the platform data */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -145,10 +143,8 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev)
dev->irq = irq;
priv->base = addr;
-#ifdef CONFIG_HAVE_CLK
priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
-#endif
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -172,10 +168,8 @@ exit_iounmap:
exit_release_mem:
release_mem_region(mem->start, resource_size(mem));
exit_free_clk:
-#ifdef CONFIG_HAVE_CLK
clk_put(clk);
exit:
-#endif
dev_err(&pdev->dev, "probe failed\n");
return ret;
@@ -196,9 +190,7 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev)
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));
-#ifdef CONFIG_HAVE_CLK
clk_put(priv->priv);
-#endif
return 0;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 734fd87cd990..62f754bd0dfe 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -2485,6 +2485,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
break;
default:
+ kfree(new_cmd);
BNX2X_ERR("Unknown command: %d\n", cmd);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 8596acaa402b..d49933ed551f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n,
#endif
while (n--) {
- pg = alloc_page(gfp);
+ pg = __skb_alloc_page(gfp, NULL);
if (unlikely(!pg)) {
q->alloc_failed++;
break;
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index f2d1ecdcaf98..8877fbfefb63 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
alloc_small_pages:
while (n--) {
- page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD);
+ page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL);
if (unlikely(!page)) {
fl->alloc_failed++;
break;
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 1050411e7ca3..b7c2d5050572 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -6235,7 +6235,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
return true;
if (!page) {
- page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+ page = __skb_alloc_page(GFP_ATOMIC, bi->skb);
bi->page = page;
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_failed++;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index c709eae58c63..4326f74f7137 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring,
/* alloc new page for storage */
if (likely(!page)) {
- page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
- ixgbe_rx_pg_order(rx_ring));
+ page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
+ bi->skb, ixgbe_rx_pg_order(rx_ring));
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_rx_page_failed++;
return false;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 3f9841d619ad..60ef64587412 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -352,7 +352,6 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter,
adapter->alloc_rx_buff_failed++;
goto no_buffers;
}
-
bi->skb = skb;
}
if (!bi->dma) {
diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index cd827ff4a021..c42bbb16cdae 100644
--- a/drivers/net/ethernet/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -6,19 +6,21 @@
* Copyright (C) 2009 Cavium Networks
*/
-#include <linux/capability.h>
+#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/capability.h>
#include <linux/interrupt.h>
-#include <linux/platform_device.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if.h>
+#include <linux/spinlock.h>
#include <linux/if_vlan.h>
+#include <linux/of_mdio.h>
+#include <linux/module.h>
+#include <linux/of_net.h>
+#include <linux/init.h>
#include <linux/slab.h>
#include <linux/phy.h>
-#include <linux/spinlock.h>
+#include <linux/io.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-mixx-defs.h>
@@ -58,8 +60,56 @@ union mgmt_port_ring_entry {
} s;
};
+#define MIX_ORING1 0x0
+#define MIX_ORING2 0x8
+#define MIX_IRING1 0x10
+#define MIX_IRING2 0x18
+#define MIX_CTL 0x20
+#define MIX_IRHWM 0x28
+#define MIX_IRCNT 0x30
+#define MIX_ORHWM 0x38
+#define MIX_ORCNT 0x40
+#define MIX_ISR 0x48
+#define MIX_INTENA 0x50
+#define MIX_REMCNT 0x58
+#define MIX_BIST 0x78
+
+#define AGL_GMX_PRT_CFG 0x10
+#define AGL_GMX_RX_FRM_CTL 0x18
+#define AGL_GMX_RX_FRM_MAX 0x30
+#define AGL_GMX_RX_JABBER 0x38
+#define AGL_GMX_RX_STATS_CTL 0x50
+
+#define AGL_GMX_RX_STATS_PKTS_DRP 0xb0
+#define AGL_GMX_RX_STATS_OCTS_DRP 0xb8
+#define AGL_GMX_RX_STATS_PKTS_BAD 0xc0
+
+#define AGL_GMX_RX_ADR_CTL 0x100
+#define AGL_GMX_RX_ADR_CAM_EN 0x108
+#define AGL_GMX_RX_ADR_CAM0 0x180
+#define AGL_GMX_RX_ADR_CAM1 0x188
+#define AGL_GMX_RX_ADR_CAM2 0x190
+#define AGL_GMX_RX_ADR_CAM3 0x198
+#define AGL_GMX_RX_ADR_CAM4 0x1a0
+#define AGL_GMX_RX_ADR_CAM5 0x1a8
+
+#define AGL_GMX_TX_STATS_CTL 0x268
+#define AGL_GMX_TX_CTL 0x270
+#define AGL_GMX_TX_STAT0 0x280
+#define AGL_GMX_TX_STAT1 0x288
+#define AGL_GMX_TX_STAT2 0x290
+#define AGL_GMX_TX_STAT3 0x298
+#define AGL_GMX_TX_STAT4 0x2a0
+#define AGL_GMX_TX_STAT5 0x2a8
+#define AGL_GMX_TX_STAT6 0x2b0
+#define AGL_GMX_TX_STAT7 0x2b8
+#define AGL_GMX_TX_STAT8 0x2c0
+#define AGL_GMX_TX_STAT9 0x2c8
+
struct octeon_mgmt {
struct net_device *netdev;
+ u64 mix;
+ u64 agl;
int port;
int irq;
u64 *tx_ring;
@@ -85,31 +135,34 @@ struct octeon_mgmt {
struct napi_struct napi;
struct tasklet_struct tx_clean_tasklet;
struct phy_device *phydev;
+ struct device_node *phy_np;
+ resource_size_t mix_phys;
+ resource_size_t mix_size;
+ resource_size_t agl_phys;
+ resource_size_t agl_size;
};
static void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable)
{
- int port = p->port;
union cvmx_mixx_intena mix_intena;
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
- mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port));
+ mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
mix_intena.s.ithena = enable ? 1 : 0;
- cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+ cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
spin_unlock_irqrestore(&p->lock, flags);
}
static void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable)
{
- int port = p->port;
union cvmx_mixx_intena mix_intena;
unsigned long flags;
spin_lock_irqsave(&p->lock, flags);
- mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port));
+ mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
mix_intena.s.othena = enable ? 1 : 0;
- cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+ cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
spin_unlock_irqrestore(&p->lock, flags);
}
@@ -146,7 +199,6 @@ static unsigned int ring_size_to_bytes(unsigned int ring_size)
static void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) {
unsigned int size;
@@ -177,24 +229,23 @@ static void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
(p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE;
p->rx_current_fill++;
/* Ring the bell. */
- cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
+ cvmx_write_csr(p->mix + MIX_IRING2, 1);
}
}
static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
{
- int port = p->port;
union cvmx_mixx_orcnt mix_orcnt;
union mgmt_port_ring_entry re;
struct sk_buff *skb;
int cleaned = 0;
unsigned long flags;
- mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+ mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
while (mix_orcnt.s.orcnt) {
spin_lock_irqsave(&p->tx_list.lock, flags);
- mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+ mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
if (mix_orcnt.s.orcnt == 0) {
spin_unlock_irqrestore(&p->tx_list.lock, flags);
@@ -214,7 +265,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
mix_orcnt.s.orcnt = 1;
/* Acknowledge to hardware that we have the buffer. */
- cvmx_write_csr(CVMX_MIXX_ORCNT(port), mix_orcnt.u64);
+ cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64);
p->tx_current_fill--;
spin_unlock_irqrestore(&p->tx_list.lock, flags);
@@ -224,7 +275,7 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
dev_kfree_skb_any(skb);
cleaned++;
- mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port));
+ mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
}
if (cleaned && netif_queue_stopped(p->netdev))
@@ -241,13 +292,12 @@ static void octeon_mgmt_clean_tx_tasklet(unsigned long arg)
static void octeon_mgmt_update_rx_stats(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
unsigned long flags;
u64 drop, bad;
/* These reads also clear the count registers. */
- drop = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port));
- bad = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port));
+ drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP);
+ bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD);
if (drop || bad) {
/* Do an atomic update. */
@@ -261,15 +311,14 @@ static void octeon_mgmt_update_rx_stats(struct net_device *netdev)
static void octeon_mgmt_update_tx_stats(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
unsigned long flags;
union cvmx_agl_gmx_txx_stat0 s0;
union cvmx_agl_gmx_txx_stat1 s1;
/* These reads also clear the count registers. */
- s0.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT0(port));
- s1.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT1(port));
+ s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0);
+ s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1);
if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) {
/* Do an atomic update. */
@@ -308,7 +357,6 @@ static u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p,
static int octeon_mgmt_receive_one(struct octeon_mgmt *p)
{
- int port = p->port;
struct net_device *netdev = p->netdev;
union cvmx_mixx_ircnt mix_ircnt;
union mgmt_port_ring_entry re;
@@ -381,18 +429,17 @@ done:
/* Tell the hardware we processed a packet. */
mix_ircnt.u64 = 0;
mix_ircnt.s.ircnt = 1;
- cvmx_write_csr(CVMX_MIXX_IRCNT(port), mix_ircnt.u64);
+ cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64);
return rc;
}
static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
{
- int port = p->port;
unsigned int work_done = 0;
union cvmx_mixx_ircnt mix_ircnt;
int rc;
- mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
+ mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
while (work_done < budget && mix_ircnt.s.ircnt) {
rc = octeon_mgmt_receive_one(p);
@@ -400,7 +447,7 @@ static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
work_done++;
/* Check for more packets. */
- mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
+ mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
}
octeon_mgmt_rx_fill_ring(p->netdev);
@@ -434,16 +481,16 @@ static void octeon_mgmt_reset_hw(struct octeon_mgmt *p)
union cvmx_agl_gmx_bist agl_gmx_bist;
mix_ctl.u64 = 0;
- cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64);
+ cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
do {
- mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(p->port));
+ mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
} while (mix_ctl.s.busy);
mix_ctl.s.reset = 1;
- cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64);
- cvmx_read_csr(CVMX_MIXX_CTL(p->port));
+ cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
+ cvmx_read_csr(p->mix + MIX_CTL);
cvmx_wait(64);
- mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(p->port));
+ mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST);
if (mix_bist.u64)
dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n",
(unsigned long long)mix_bist.u64);
@@ -474,7 +521,6 @@ static void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs,
static void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
union cvmx_agl_gmx_rxx_adr_ctl adr_ctl;
union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
unsigned long flags;
@@ -520,29 +566,29 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
spin_lock_irqsave(&p->lock, flags);
/* Disable packet I/O. */
- agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+ agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
prev_packet_enable = agl_gmx_prtx.s.en;
agl_gmx_prtx.s.en = 0;
- cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
adr_ctl.u64 = 0;
adr_ctl.s.cam_mode = cam_mode;
adr_ctl.s.mcst = multicast_mode;
adr_ctl.s.bcst = 1; /* Allow broadcast */
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), adr_ctl.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), cam_state.cam[0]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), cam_state.cam[1]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), cam_state.cam[2]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), cam_state.cam[3]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), cam_state.cam[4]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), cam_state.cam[5]);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), cam_state.cam_mask);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask);
/* Restore packet I/O. */
agl_gmx_prtx.s.en = prev_packet_enable;
- cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
spin_unlock_irqrestore(&p->lock, flags);
}
@@ -564,7 +610,6 @@ static int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr)
static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM;
/*
@@ -580,8 +625,8 @@ static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
netdev->mtu = new_mtu;
- cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port),
+ cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, size_without_fcs);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER,
(size_without_fcs + 7) & 0xfff8);
return 0;
@@ -591,14 +636,13 @@ static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
{
struct net_device *netdev = dev_id;
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
union cvmx_mixx_isr mixx_isr;
- mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(port));
+ mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR);
/* Clear any pending interrupts */
- cvmx_write_csr(CVMX_MIXX_ISR(port), mixx_isr.u64);
- cvmx_read_csr(CVMX_MIXX_ISR(port));
+ cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64);
+ cvmx_read_csr(p->mix + MIX_ISR);
if (mixx_isr.s.irthresh) {
octeon_mgmt_disable_rx_irq(p);
@@ -629,7 +673,6 @@ static int octeon_mgmt_ioctl(struct net_device *netdev,
static void octeon_mgmt_adjust_link(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
union cvmx_agl_gmx_prtx_cfg prtx_cfg;
unsigned long flags;
int link_changed = 0;
@@ -640,11 +683,9 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev)
link_changed = 1;
if (p->last_duplex != p->phydev->duplex) {
p->last_duplex = p->phydev->duplex;
- prtx_cfg.u64 =
- cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+ prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
prtx_cfg.s.duplex = p->phydev->duplex;
- cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port),
- prtx_cfg.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
}
} else {
if (p->last_link)
@@ -670,18 +711,16 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev)
static int octeon_mgmt_init_phy(struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- char phy_id[MII_BUS_ID_SIZE + 3];
- if (octeon_is_simulation()) {
+ if (octeon_is_simulation() || p->phy_np == NULL) {
/* No PHYs in the simulator. */
netif_carrier_on(netdev);
return 0;
}
- snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", p->port);
-
- p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0,
- PHY_INTERFACE_MODE_MII);
+ p->phydev = of_phy_connect(netdev, p->phy_np,
+ octeon_mgmt_adjust_link, 0,
+ PHY_INTERFACE_MODE_MII);
if (IS_ERR(p->phydev)) {
p->phydev = NULL;
@@ -737,14 +776,14 @@ static int octeon_mgmt_open(struct net_device *netdev)
octeon_mgmt_reset_hw(p);
- mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
+ mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
/* Bring it out of reset if needed. */
if (mix_ctl.s.reset) {
mix_ctl.s.reset = 0;
- cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
+ cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
do {
- mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
+ mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
} while (mix_ctl.s.reset);
}
@@ -755,17 +794,17 @@ static int octeon_mgmt_open(struct net_device *netdev)
oring1.u64 = 0;
oring1.s.obase = p->tx_ring_handle >> 3;
oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE;
- cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64);
+ cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64);
iring1.u64 = 0;
iring1.s.ibase = p->rx_ring_handle >> 3;
iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE;
- cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64);
+ cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64);
/* Disable packet I/O. */
- prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+ prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
prtx_cfg.s.en = 0;
- cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN);
octeon_mgmt_set_mac_address(netdev, &sa);
@@ -782,7 +821,7 @@ static int octeon_mgmt_open(struct net_device *netdev)
mix_ctl.s.nbtarb = 0; /* Arbitration mode */
/* MII CB-request FIFO programmable high watermark */
mix_ctl.s.mrq_hwm = 1;
- cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
+ cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
|| OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) {
@@ -809,16 +848,16 @@ static int octeon_mgmt_open(struct net_device *netdev)
/* Clear statistics. */
/* Clear on read. */
- cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_CTL(port), 1);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port), 0);
- cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port), 0);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0);
- cvmx_write_csr(CVMX_AGL_GMX_TXX_STATS_CTL(port), 1);
- cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT0(port), 0);
- cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT1(port), 0);
+ cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1);
+ cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0);
+ cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0);
/* Clear any pending interrupts */
- cvmx_write_csr(CVMX_MIXX_ISR(port), cvmx_read_csr(CVMX_MIXX_ISR(port)));
+ cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR));
if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name,
netdev)) {
@@ -829,18 +868,18 @@ static int octeon_mgmt_open(struct net_device *netdev)
/* Interrupt every single RX packet */
mix_irhwm.u64 = 0;
mix_irhwm.s.irhwm = 0;
- cvmx_write_csr(CVMX_MIXX_IRHWM(port), mix_irhwm.u64);
+ cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64);
/* Interrupt when we have 1 or more packets to clean. */
mix_orhwm.u64 = 0;
mix_orhwm.s.orhwm = 1;
- cvmx_write_csr(CVMX_MIXX_ORHWM(port), mix_orhwm.u64);
+ cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64);
/* Enable receive and transmit interrupts */
mix_intena.u64 = 0;
mix_intena.s.ithena = 1;
mix_intena.s.othena = 1;
- cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64);
+ cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
/* Enable packet I/O. */
@@ -871,7 +910,7 @@ static int octeon_mgmt_open(struct net_device *netdev)
* frame. GMX checks that the PREAMBLE is sent correctly.
*/
rxx_frm_ctl.s.pre_chk = 1;
- cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
/* Enable the AGL block */
agl_gmx_inf_mode.u64 = 0;
@@ -879,13 +918,13 @@ static int octeon_mgmt_open(struct net_device *netdev)
cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
/* Configure the port duplex and enables */
- prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
+ prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
prtx_cfg.s.tx_en = 1;
prtx_cfg.s.rx_en = 1;
prtx_cfg.s.en = 1;
p->last_duplex = 1;
prtx_cfg.s.duplex = p->last_duplex;
- cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64);
+ cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
p->last_link = 0;
netif_carrier_off(netdev);
@@ -949,7 +988,6 @@ static int octeon_mgmt_stop(struct net_device *netdev)
static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- int port = p->port;
union mgmt_port_ring_entry re;
unsigned long flags;
int rv = NETDEV_TX_BUSY;
@@ -993,7 +1031,7 @@ static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
netdev->stats.tx_bytes += skb->len;
/* Ring the bell. */
- cvmx_write_csr(CVMX_MIXX_ORING2(port), 1);
+ cvmx_write_csr(p->mix + MIX_ORING2, 1);
rv = NETDEV_TX_OK;
out:
@@ -1071,10 +1109,14 @@ static const struct net_device_ops octeon_mgmt_ops = {
static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
{
- struct resource *res_irq;
struct net_device *netdev;
struct octeon_mgmt *p;
- int i;
+ const __be32 *data;
+ const u8 *mac;
+ struct resource *res_mix;
+ struct resource *res_agl;
+ int len;
+ int result;
netdev = alloc_etherdev(sizeof(struct octeon_mgmt));
if (netdev == NULL)
@@ -1088,14 +1130,63 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
p->netdev = netdev;
p->dev = &pdev->dev;
- p->port = pdev->id;
+ data = of_get_property(pdev->dev.of_node, "cell-index", &len);
+ if (data && len == sizeof(*data)) {
+ p->port = be32_to_cpup(data);
+ } else {
+ dev_err(&pdev->dev, "no 'cell-index' property\n");
+ result = -ENXIO;
+ goto err;
+ }
+
snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port);
- res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res_irq)
+ result = platform_get_irq(pdev, 0);
+ if (result < 0)
+ goto err;
+
+ p->irq = result;
+
+ res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res_mix == NULL) {
+ dev_err(&pdev->dev, "no 'reg' resource\n");
+ result = -ENXIO;
+ goto err;
+ }
+
+ res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res_agl == NULL) {
+ dev_err(&pdev->dev, "no 'reg' resource\n");
+ result = -ENXIO;
+ goto err;
+ }
+
+ p->mix_phys = res_mix->start;
+ p->mix_size = resource_size(res_mix);
+ p->agl_phys = res_agl->start;
+ p->agl_size = resource_size(res_agl);
+
+
+ if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size,
+ res_mix->name)) {
+ dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
+ res_mix->name);
+ result = -ENXIO;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size,
+ res_agl->name)) {
+ result = -ENXIO;
+ dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
+ res_agl->name);
goto err;
+ }
+
+
+ p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size);
+ p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size);
- p->irq = res_irq->start;
spin_lock_init(&p->lock);
skb_queue_head_init(&p->tx_list);
@@ -1108,24 +1199,26 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
netdev->netdev_ops = &octeon_mgmt_ops;
netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
- /* The mgmt ports get the first N MACs. */
- for (i = 0; i < 6; i++)
- netdev->dev_addr[i] = octeon_bootinfo->mac_addr_base[i];
- netdev->dev_addr[5] += p->port;
+ mac = of_get_mac_address(pdev->dev.of_node);
+
+ if (mac)
+ memcpy(netdev->dev_addr, mac, 6);
- if (p->port >= octeon_bootinfo->mac_addr_count)
- dev_err(&pdev->dev,
- "Error %s: Using MAC outside of the assigned range: %pM\n",
- netdev->name, netdev->dev_addr);
+ p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
- if (register_netdev(netdev))
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ result = register_netdev(netdev);
+ if (result)
goto err;
dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
return 0;
+
err:
free_netdev(netdev);
- return -ENOENT;
+ return result;
}
static int __devexit octeon_mgmt_remove(struct platform_device *pdev)
@@ -1137,10 +1230,19 @@ static int __devexit octeon_mgmt_remove(struct platform_device *pdev)
return 0;
}
+static struct of_device_id octeon_mgmt_match[] = {
+ {
+ .compatible = "cavium,octeon-5750-mix",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, octeon_mgmt_match);
+
static struct platform_driver octeon_mgmt_driver = {
.driver = {
.name = "octeon_mgmt",
.owner = THIS_MODULE,
+ .of_match_table = octeon_mgmt_match,
},
.probe = octeon_mgmt_probe,
.remove = __devexit_p(octeon_mgmt_remove),
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 70554a1b2b02..65a8d49106a4 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1503,6 +1503,11 @@ static int efx_probe_all(struct efx_nic *efx)
goto fail2;
}
+ BUILD_BUG_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_RXQ_MIN_ENT);
+ if (WARN_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_TXQ_MIN_ENT(efx))) {
+ rc = -EINVAL;
+ goto fail3;
+ }
efx->rxq_entries = efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE;
rc = efx_probe_filters(efx);
@@ -2070,6 +2075,7 @@ static int efx_register_netdev(struct efx_nic *efx)
net_dev->irq = efx->pci_dev->irq;
net_dev->netdev_ops = &efx_netdev_ops;
SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+ net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
rtnl_lock();
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index be8f9158a714..70755c97251a 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -30,6 +30,7 @@ extern netdev_tx_t
efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
+extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
/* RX */
extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
@@ -52,10 +53,15 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
#define EFX_MAX_EVQ_SIZE 16384UL
#define EFX_MIN_EVQ_SIZE 512UL
-/* The smallest [rt]xq_entries that the driver supports. Callers of
- * efx_wake_queue() assume that they can subsequently send at least one
- * skb. Falcon/A1 may require up to three descriptors per skb_frag. */
-#define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS))
+/* Maximum number of TCP segments we support for soft-TSO */
+#define EFX_TSO_MAX_SEGS 100
+
+/* The smallest [rt]xq_entries that the driver supports. RX minimum
+ * is a bit arbitrary. For TX, we must have space for at least 2
+ * TSO skbs.
+ */
+#define EFX_RXQ_MIN_ENT 128U
+#define EFX_TXQ_MIN_ENT(efx) (2 * efx_tx_max_skb_descs(efx))
/* Filters */
extern int efx_probe_filters(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index 10536f93b561..8cba2df82b18 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -680,21 +680,27 @@ static int efx_ethtool_set_ringparam(struct net_device *net_dev,
struct ethtool_ringparam *ring)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ u32 txq_entries;
if (ring->rx_mini_pending || ring->rx_jumbo_pending ||
ring->rx_pending > EFX_MAX_DMAQ_SIZE ||
ring->tx_pending > EFX_MAX_DMAQ_SIZE)
return -EINVAL;
- if (ring->rx_pending < EFX_MIN_RING_SIZE ||
- ring->tx_pending < EFX_MIN_RING_SIZE) {
+ if (ring->rx_pending < EFX_RXQ_MIN_ENT) {
netif_err(efx, drv, efx->net_dev,
- "TX and RX queues cannot be smaller than %ld\n",
- EFX_MIN_RING_SIZE);
+ "RX queues cannot be smaller than %u\n",
+ EFX_RXQ_MIN_ENT);
return -EINVAL;
}
- return efx_realloc_channels(efx, ring->rx_pending, ring->tx_pending);
+ txq_entries = max(ring->tx_pending, EFX_TXQ_MIN_ENT(efx));
+ if (txq_entries != ring->tx_pending)
+ netif_warn(efx, drv, efx->net_dev,
+ "increasing TX queue size to minimum of %u\n",
+ txq_entries);
+
+ return efx_realloc_channels(efx, ring->rx_pending, txq_entries);
}
static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 9b225a7769f7..18713436b443 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -119,6 +119,25 @@ efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr)
return len;
}
+unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
+{
+ /* Header and payload descriptor for each output segment, plus
+ * one for every input fragment boundary within a segment
+ */
+ unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
+
+ /* Possibly one more per segment for the alignment workaround */
+ if (EFX_WORKAROUND_5391(efx))
+ max_descs += EFX_TSO_MAX_SEGS;
+
+ /* Possibly more for PCIe page boundaries within input fragments */
+ if (PAGE_SIZE > EFX_PAGE_SIZE)
+ max_descs += max_t(unsigned int, MAX_SKB_FRAGS,
+ DIV_ROUND_UP(GSO_MAX_SIZE, EFX_PAGE_SIZE));
+
+ return max_descs;
+}
+
/*
* Add a socket buffer to a TX queue
*
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index ab4c376cb276..f2d3665430ad 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -82,9 +82,7 @@ struct stmmac_priv {
struct stmmac_counters mmc;
struct dma_features dma_cap;
int hw_cap_support;
-#ifdef CONFIG_HAVE_CLK
struct clk *stmmac_clk;
-#endif
int clk_csr;
int synopsys_id;
struct timer_list eee_ctrl_timer;
@@ -113,46 +111,6 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
void stmmac_disable_eee_mode(struct stmmac_priv *priv);
bool stmmac_eee_init(struct stmmac_priv *priv);
-#ifdef CONFIG_HAVE_CLK
-static inline int stmmac_clk_enable(struct stmmac_priv *priv)
-{
- if (!IS_ERR(priv->stmmac_clk))
- return clk_prepare_enable(priv->stmmac_clk);
-
- return 0;
-}
-
-static inline void stmmac_clk_disable(struct stmmac_priv *priv)
-{
- if (IS_ERR(priv->stmmac_clk))
- return;
-
- clk_disable_unprepare(priv->stmmac_clk);
-}
-static inline int stmmac_clk_get(struct stmmac_priv *priv)
-{
- priv->stmmac_clk = clk_get(priv->device, NULL);
-
- if (IS_ERR(priv->stmmac_clk))
- return PTR_ERR(priv->stmmac_clk);
-
- return 0;
-}
-#else
-static inline int stmmac_clk_enable(struct stmmac_priv *priv)
-{
- return 0;
-}
-static inline void stmmac_clk_disable(struct stmmac_priv *priv)
-{
-}
-static inline int stmmac_clk_get(struct stmmac_priv *priv)
-{
- return 0;
-}
-#endif /* CONFIG_HAVE_CLK */
-
-
#ifdef CONFIG_STMMAC_PLATFORM
extern struct platform_driver stmmac_pltfr_driver;
static inline int stmmac_register_platform(void)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f6b04c1a3672..fd8882f9602a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -28,6 +28,7 @@
https://bugzilla.stlinux.com/
*******************************************************************************/
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/ip.h>
@@ -173,12 +174,8 @@ static void stmmac_verify_args(void)
static void stmmac_clk_csr_set(struct stmmac_priv *priv)
{
-#ifdef CONFIG_HAVE_CLK
u32 clk_rate;
- if (IS_ERR(priv->stmmac_clk))
- return;
-
clk_rate = clk_get_rate(priv->stmmac_clk);
/* Platform provided default clk_csr would be assumed valid
@@ -200,7 +197,6 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
* we can not estimate the proper divider as it is not known
* the frequency of clk_csr_i. So we do not change the default
* divider. */
-#endif
}
#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG)
@@ -1070,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
} else
priv->tm->enable = 1;
#endif
- stmmac_clk_enable(priv);
+ clk_enable(priv->stmmac_clk);
stmmac_check_ether_addr(priv);
@@ -1192,7 +1188,7 @@ open_error:
if (priv->phydev)
phy_disconnect(priv->phydev);
- stmmac_clk_disable(priv);
+ clk_disable(priv->stmmac_clk);
return ret;
}
@@ -1250,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
#ifdef CONFIG_STMMAC_DEBUG_FS
stmmac_exit_fs();
#endif
- stmmac_clk_disable(priv);
+ clk_disable(priv->stmmac_clk);
return 0;
}
@@ -2078,11 +2074,14 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
ret = register_netdev(ndev);
if (ret) {
pr_err("%s: ERROR %i registering the device\n", __func__, ret);
- goto error;
+ goto error_netdev_register;
}
- if (stmmac_clk_get(priv))
+ priv->stmmac_clk = clk_get(priv->device, NULL);
+ if (IS_ERR(priv->stmmac_clk)) {
pr_warning("%s: warning: cannot get CSR clock\n", __func__);
+ goto error_clk_get;
+ }
/* If a specific clk_csr value is passed from the platform
* this means that the CSR Clock Range selection cannot be
@@ -2100,15 +2099,17 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
if (ret < 0) {
pr_debug("%s: MDIO bus (id: %d) registration failed",
__func__, priv->plat->bus_id);
- goto error;
+ goto error_mdio_register;
}
return priv;
-error:
- netif_napi_del(&priv->napi);
-
+error_mdio_register:
+ clk_put(priv->stmmac_clk);
+error_clk_get:
unregister_netdev(ndev);
+error_netdev_register:
+ netif_napi_del(&priv->napi);
free_netdev(ndev);
return NULL;
@@ -2177,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
else {
stmmac_set_mac(priv->ioaddr, false);
/* Disable clock in case of PWM is off */
- stmmac_clk_disable(priv);
+ clk_disable(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
@@ -2202,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
priv->hw->mac->pmt(priv->ioaddr, 0);
else
/* enable the clk prevously disabled */
- stmmac_clk_enable(priv);
+ clk_enable(priv->stmmac_clk);
netif_device_attach(ndev);
diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c
index 826d961f39f7..d4015aa663e6 100644
--- a/drivers/net/phy/mdio-octeon.c
+++ b/drivers/net/phy/mdio-octeon.c
@@ -3,14 +3,17 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2009 Cavium Networks
+ * Copyright (C) 2009,2011 Cavium, Inc.
*/
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
#include <linux/phy.h>
+#include <linux/io.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-smix-defs.h>
@@ -18,9 +21,17 @@
#define DRV_VERSION "1.0"
#define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
+#define SMI_CMD 0x0
+#define SMI_WR_DAT 0x8
+#define SMI_RD_DAT 0x10
+#define SMI_CLK 0x18
+#define SMI_EN 0x20
+
struct octeon_mdiobus {
struct mii_bus *mii_bus;
- int unit;
+ u64 register_base;
+ resource_size_t mdio_phys;
+ resource_size_t regsize;
int phy_irq[PHY_MAX_ADDR];
};
@@ -35,15 +46,15 @@ static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum;
- cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
+ cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
do {
/*
* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads.
*/
- cvmx_wait(1000);
- smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(p->unit));
+ __delay(1000);
+ smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT);
} while (smi_rd.s.pending && --timeout);
if (smi_rd.s.val)
@@ -62,21 +73,21 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
smi_wr.u64 = 0;
smi_wr.s.dat = val;
- cvmx_write_csr(CVMX_SMIX_WR_DAT(p->unit), smi_wr.u64);
+ cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
smi_cmd.u64 = 0;
smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum;
- cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64);
+ cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
do {
/*
* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads.
*/
- cvmx_wait(1000);
- smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(p->unit));
+ __delay(1000);
+ smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
} while (smi_wr.s.pending && --timeout);
if (timeout <= 0)
@@ -88,38 +99,44 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
static int __devinit octeon_mdiobus_probe(struct platform_device *pdev)
{
struct octeon_mdiobus *bus;
+ struct resource *res_mem;
union cvmx_smix_en smi_en;
- int i;
int err = -ENOENT;
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
- /* The platform_device id is our unit number. */
- bus->unit = pdev->id;
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (res_mem == NULL) {
+ dev_err(&pdev->dev, "found no memory resource\n");
+ err = -ENXIO;
+ goto fail;
+ }
+ bus->mdio_phys = res_mem->start;
+ bus->regsize = resource_size(res_mem);
+ if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize,
+ res_mem->name)) {
+ dev_err(&pdev->dev, "request_mem_region failed\n");
+ goto fail;
+ }
+ bus->register_base =
+ (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize);
bus->mii_bus = mdiobus_alloc();
if (!bus->mii_bus)
- goto err;
+ goto fail;
smi_en.u64 = 0;
smi_en.s.en = 1;
- cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
-
- /*
- * Standard Octeon evaluation boards don't support phy
- * interrupts, we need to poll.
- */
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bus->phy_irq[i] = PHY_POLL;
+ cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
bus->mii_bus->priv = bus;
bus->mii_bus->irq = bus->phy_irq;
bus->mii_bus->name = "mdio-octeon";
- snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
- bus->mii_bus->name, bus->unit);
+ snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
bus->mii_bus->parent = &pdev->dev;
bus->mii_bus->read = octeon_mdiobus_read;
@@ -127,20 +144,18 @@ static int __devinit octeon_mdiobus_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, bus);
- err = mdiobus_register(bus->mii_bus);
+ err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
if (err)
- goto err_register;
+ goto fail_register;
dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
return 0;
-err_register:
+fail_register:
mdiobus_free(bus->mii_bus);
-
-err:
- devm_kfree(&pdev->dev, bus);
+fail:
smi_en.u64 = 0;
- cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
+ cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
return err;
}
@@ -154,14 +169,23 @@ static int __devexit octeon_mdiobus_remove(struct platform_device *pdev)
mdiobus_unregister(bus->mii_bus);
mdiobus_free(bus->mii_bus);
smi_en.u64 = 0;
- cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64);
+ cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
return 0;
}
+static struct of_device_id octeon_mdiobus_match[] = {
+ {
+ .compatible = "cavium,octeon-3860-mdio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, octeon_mdiobus_match);
+
static struct platform_driver octeon_mdiobus_driver = {
.driver = {
.name = "mdio-octeon",
.owner = THIS_MODULE,
+ .of_match_table = octeon_mdiobus_match,
},
.probe = octeon_mdiobus_probe,
.remove = __devexit_p(octeon_mdiobus_remove),
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 187c144c5e5b..64610048ce87 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)
struct page *page;
int err;
- page = alloc_page(gfp_flags);
+ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index abb520dec032..e361afed99ff 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -342,7 +342,7 @@ static int at76_dfu_get_status(struct usb_device *udev,
return ret;
}
-static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
+static int at76_dfu_get_state(struct usb_device *udev, u8 *state)
{
int ret;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 9d48f9a461e1..a0a202de1109 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2057,9 +2057,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
void
ath5k_beacon_config(struct ath5k_hw *ah)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ah->bmisscount = 0;
ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
@@ -2086,7 +2084,7 @@ ath5k_beacon_config(struct ath5k_hw *ah)
ath5k_hw_set_imr(ah, ah->imask);
mmiowb();
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
static void ath5k_tasklet_beacon(unsigned long data)
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index 4026c906cc7b..b7e0258887e7 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -1482,7 +1482,7 @@ ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
case AR5K_EEPROM_MODE_11A:
offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
rate_pcal_info = ee->ee_rate_tpwr_a;
- ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_RATE_CHAN;
break;
case AR5K_EEPROM_MODE_11B:
offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h
index dc2bcfeadeb4..94a9bbea6874 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.h
+++ b/drivers/net/wireless/ath/ath5k/eeprom.h
@@ -182,6 +182,7 @@
#define AR5K_EEPROM_EEP_DELTA 10
#define AR5K_EEPROM_N_MODES 3
#define AR5K_EEPROM_N_5GHZ_CHAN 10
+#define AR5K_EEPROM_N_5GHZ_RATE_CHAN 8
#define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index c89fa6ead615..df61a09adb6d 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -255,7 +255,6 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ath5k_vif *avf = (void *)vif->drv_priv;
struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
- unsigned long flags;
mutex_lock(&ah->lock);
@@ -301,9 +300,9 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
if (changes & BSS_CHANGED_BEACON) {
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ath5k_beacon_update(hw, vif);
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
if (changes & BSS_CHANGED_BEACON_ENABLED)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index 2c9f7d7ed4cc..0ed3846f9cbb 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -142,6 +142,7 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
};
int training_power;
int i, val;
+ u32 am2pm_mask = ah->paprd_ratemask;
if (IS_CHAN_2GHZ(ah->curchan))
training_power = ar9003_get_training_power_2g(ah);
@@ -158,10 +159,13 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
}
ah->paprd_training_power = training_power;
+ if (AR_SREV_9330(ah))
+ am2pm_mask = 0;
+
REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK,
ah->paprd_ratemask);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK,
- ah->paprd_ratemask);
+ am2pm_mask);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK,
ah->paprd_ratemask_ht40);
@@ -782,6 +786,102 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
}
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
+static bool ar9003_paprd_retrain_pa_in(struct ath_hw *ah,
+ struct ath9k_hw_cal_data *caldata,
+ int chain)
+{
+ u32 *pa_in = caldata->pa_table[chain];
+ int capdiv_offset, quick_drop_offset;
+ int capdiv2g, quick_drop;
+ int count = 0;
+ int i;
+
+ if (!AR_SREV_9485(ah) && !AR_SREV_9330(ah))
+ return false;
+
+ capdiv2g = REG_READ_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+ AR_PHY_65NM_CH0_TXRF3_CAPDIV2G);
+
+ quick_drop = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+ AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP);
+
+ if (quick_drop)
+ quick_drop -= 0x40;
+
+ for (i = 0; i < NUM_BIN + 1; i++) {
+ if (pa_in[i] == 1400)
+ count++;
+ }
+
+ if (AR_SREV_9485(ah)) {
+ if (pa_in[23] < 800) {
+ capdiv_offset = (int)((1000 - pa_in[23] + 75) / 150);
+ capdiv2g += capdiv_offset;
+ if (capdiv2g > 7) {
+ capdiv2g = 7;
+ if (pa_in[23] < 600) {
+ quick_drop++;
+ if (quick_drop > 0)
+ quick_drop = 0;
+ }
+ }
+ } else if (pa_in[23] == 1400) {
+ quick_drop_offset = min_t(int, count / 3, 2);
+ quick_drop += quick_drop_offset;
+ capdiv2g += quick_drop_offset / 2;
+
+ if (capdiv2g > 7)
+ capdiv2g = 7;
+
+ if (quick_drop > 0) {
+ quick_drop = 0;
+ capdiv2g -= quick_drop_offset;
+ if (capdiv2g < 0)
+ capdiv2g = 0;
+ }
+ } else {
+ return false;
+ }
+ } else if (AR_SREV_9330(ah)) {
+ if (pa_in[23] < 1000) {
+ capdiv_offset = (1000 - pa_in[23]) / 100;
+ capdiv2g += capdiv_offset;
+ if (capdiv_offset > 3) {
+ capdiv_offset = 1;
+ quick_drop--;
+ }
+
+ capdiv2g += capdiv_offset;
+ if (capdiv2g > 6)
+ capdiv2g = 6;
+ if (quick_drop < -4)
+ quick_drop = -4;
+ } else if (pa_in[23] == 1400) {
+ if (count > 3) {
+ quick_drop++;
+ capdiv2g -= count / 4;
+ if (quick_drop > -2)
+ quick_drop = -2;
+ } else {
+ capdiv2g--;
+ }
+
+ if (capdiv2g < 0)
+ capdiv2g = 0;
+ } else {
+ return false;
+ }
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+ AR_PHY_65NM_CH0_TXRF3_CAPDIV2G, capdiv2g);
+ REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+ AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
+ quick_drop);
+
+ return true;
+}
+
int ar9003_paprd_create_curve(struct ath_hw *ah,
struct ath9k_hw_cal_data *caldata, int chain)
{
@@ -817,6 +917,9 @@ int ar9003_paprd_create_curve(struct ath_hw *ah,
if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain))
status = -2;
+ if (ar9003_paprd_retrain_pa_in(ah, caldata, chain))
+ status = -EINPROGRESS;
+
REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 7bfbaf065a43..84d3d4956861 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -625,6 +625,10 @@
#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0)
#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc)
+#define AR_PHY_65NM_CH0_TXRF3 0x16048
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G 0x0000001e
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S 1
+
#define AR_PHY_65NM_CH0_SYNTH4 0x1608c
#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT (AR_SREV_9462(ah) ? 0x00000001 : 0x00000002)
#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S (AR_SREV_9462(ah) ? 0 : 1)
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index bacdb8fb4ef4..9f83f71742a5 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -341,7 +341,8 @@ void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
{
struct ath_btcoex *btcoex = &sc->btcoex;
- ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+ if (btcoex->hw_timer_enabled)
+ ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
}
u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cfa91ab7acf8..48af40151d23 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -463,9 +463,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.spurchans[i][1] = AR_NO_SPUR;
}
- /* PAPRD needs some more work to be enabled */
- ah->config.paprd_disable = 1;
-
ah->config.rx_intr_mitigation = true;
ah->config.pcieSerDesWrite = true;
@@ -730,6 +727,7 @@ int ath9k_hw_init(struct ath_hw *ah)
case AR9300_DEVID_QCA955X:
case AR9300_DEVID_AR9580:
case AR9300_DEVID_AR9462:
+ case AR9485_DEVID_AR1111:
break;
default:
if (common->bus_ops->ath_bus_type == ATH_USB)
@@ -977,9 +975,6 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
else
imr_reg |= AR_IMR_TXOK;
- if (opmode == NL80211_IFTYPE_AP)
- imr_reg |= AR_IMR_MIB;
-
ENABLE_REGWRITE_BUFFER(ah);
REG_WRITE(ah, AR_IMR, imr_reg);
@@ -1777,6 +1772,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
/* Operating channel changed, reset channel calibration data */
memset(caldata, 0, sizeof(*caldata));
ath9k_init_nfcal_hist_buffer(ah, chan);
+ } else if (caldata) {
+ caldata->paprd_packet_sent = false;
}
ah->noise = ath9k_hw_getchan_noise(ah, chan);
@@ -2501,7 +2498,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->tx_desc_len = sizeof(struct ar9003_txc);
pCap->txs_len = sizeof(struct ar9003_txs);
if (!ah->config.paprd_disable &&
- ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
+ ah->eep_ops->get_eeprom(ah, EEP_PAPRD) &&
+ !AR_SREV_9462(ah))
pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
} else {
pCap->tx_desc_len = sizeof(struct ath_desc);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index dd0c146d81dc..6599a75f01fe 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -49,6 +49,7 @@
#define AR9300_DEVID_AR9462 0x0034
#define AR9300_DEVID_AR9330 0x0035
#define AR9300_DEVID_QCA955X 0x0038
+#define AR9485_DEVID_AR1111 0x0037
#define AR5416_AR9100_DEVID 0x000b
@@ -404,6 +405,7 @@ struct ath9k_hw_cal_data {
int8_t iCoff;
int8_t qCoff;
bool rtt_done;
+ bool paprd_packet_sent;
bool paprd_done;
bool nfcal_pending;
bool nfcal_interference;
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index d4549e9aac5c..825a29cc9313 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -254,8 +254,9 @@ void ath_paprd_calibrate(struct work_struct *work)
int chain_ok = 0;
int chain;
int len = 1800;
+ int ret;
- if (!caldata)
+ if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done)
return;
ath9k_ps_wakeup(sc);
@@ -282,13 +283,6 @@ void ath_paprd_calibrate(struct work_struct *work)
continue;
chain_ok = 0;
-
- ath_dbg(common, CALIBRATE,
- "Sending PAPRD frame for thermal measurement on chain %d\n",
- chain);
- if (!ath_paprd_send_frame(sc, skb, chain))
- goto fail_paprd;
-
ar9003_paprd_setup_gain_table(ah, chain);
ath_dbg(common, CALIBRATE,
@@ -302,7 +296,13 @@ void ath_paprd_calibrate(struct work_struct *work)
break;
}
- if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+ ret = ar9003_paprd_create_curve(ah, caldata, chain);
+ if (ret == -EINPROGRESS) {
+ ath_dbg(common, CALIBRATE,
+ "PAPRD curve on chain %d needs to be re-trained\n",
+ chain);
+ break;
+ } else if (ret) {
ath_dbg(common, CALIBRATE,
"PAPRD create curve failed on chain %d\n",
chain);
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 7990cd55599c..b42be910a83d 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -773,15 +773,10 @@ bool ath9k_hw_intrpend(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_intrpend);
-void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+void ath9k_hw_kill_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- if (!(ah->imask & ATH9K_INT_GLOBAL))
- atomic_set(&ah->intr_ref_cnt, -1);
- else
- atomic_dec(&ah->intr_ref_cnt);
-
ath_dbg(common, INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
@@ -793,6 +788,17 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
(void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
}
}
+EXPORT_SYMBOL(ath9k_hw_kill_interrupts);
+
+void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+{
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ atomic_set(&ah->intr_ref_cnt, -1);
+ else
+ atomic_dec(&ah->intr_ref_cnt);
+
+ ath9k_hw_kill_interrupts(ah);
+}
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
void ath9k_hw_enable_interrupts(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 0eba36dca6f8..4a745e68dd94 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -738,6 +738,7 @@ bool ath9k_hw_intrpend(struct ath_hw *ah);
void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
+void ath9k_hw_kill_interrupts(struct ath_hw *ah);
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4d8dc9ff5a75..8a2b04d5922f 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -462,8 +462,10 @@ irqreturn_t ath_isr(int irq, void *dev)
if (!ath9k_hw_intrpend(ah))
return IRQ_NONE;
- if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
+ ath9k_hw_kill_interrupts(ah);
return IRQ_HANDLED;
+ }
/*
* Figure out the reason(s) for the interrupt. Note
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 87b89d55e637..a978984d78a5 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -37,6 +37,7 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
{ PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */
+ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */
{ 0 }
};
@@ -320,6 +321,7 @@ static int ath_pci_suspend(struct device *device)
* Otherwise the chip never moved to full sleep,
* when no interface is up.
*/
+ ath9k_stop_btcoex(sc);
ath9k_hw_disable(sc->sc_ah);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 12aca02228c2..4480c0cc655f 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1044,7 +1044,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_hdr *hdr;
int retval;
- bool decrypt_error = false;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
@@ -1066,6 +1065,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
tsf_lower = tsf & 0xffffffff;
do {
+ bool decrypt_error = false;
/* If handling rx interrupt and flush is in progress => exit */
if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
break;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index b074c3a2cde1..b088fa0eb022 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2019,6 +2019,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
+ if (sc->sc_ah->caldata)
+ sc->sc_ah->caldata->paprd_packet_sent = true;
+
if (!(tx_flags & ATH_TX_ERROR))
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index e9a8f00e195c..73730e94e0ac 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2719,32 +2719,37 @@ static int b43_gpio_init(struct b43_wldev *dev)
if (dev->dev->chip_id == 0x4301) {
mask |= 0x0060;
set |= 0x0060;
+ } else if (dev->dev->chip_id == 0x5354) {
+ /* Don't allow overtaking buttons GPIOs */
+ set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
}
- if (dev->dev->chip_id == 0x5354)
- set &= 0xff02;
+
if (0 /* FIXME: conditional unknown */ ) {
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0100);
- mask |= 0x0180;
- set |= 0x0180;
+ /* BT Coexistance Input */
+ mask |= 0x0080;
+ set |= 0x0080;
+ /* BT Coexistance Out */
+ mask |= 0x0100;
+ set |= 0x0100;
}
if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
+ /* PA is controlled by gpio 9, let ucode handle it */
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0200);
mask |= 0x0200;
set |= 0x0200;
}
- if (dev->dev->core_rev >= 2)
- mask |= 0x0010; /* FIXME: This is redundant. */
switch (dev->dev->bus_type) {
#ifdef CONFIG_B43_BCMA
case B43_BUS_BCMA:
bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
(bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
- BCMA_CC_GPIOCTL) & mask) | set);
+ BCMA_CC_GPIOCTL) & ~mask) | set);
break;
#endif
#ifdef CONFIG_B43_SSB
@@ -2753,7 +2758,7 @@ static int b43_gpio_init(struct b43_wldev *dev)
if (gpiodev)
ssb_write32(gpiodev, B43_GPIO_CONTROL,
(ssb_read32(gpiodev, B43_GPIO_CONTROL)
- & mask) | set);
+ & ~mask) | set);
break;
#endif
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index dcfeb1a4ef8b..7f9ba82a586a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -1196,7 +1196,7 @@ exit:
kfree(buf);
/* close file before return */
if (fp)
- filp_close(fp, current->files);
+ filp_close(fp, NULL);
/* restore previous address limit */
set_fs(old_fs);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index 78fb17e63eb7..7fe68aa69dfd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -521,7 +521,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
else
devinfo->bus_pub.bus->dstats.tx_errors++;
- dev_kfree_skb(req->skb);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
@@ -542,7 +542,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
devinfo->bus_pub.bus->dstats.rx_packets++;
} else {
devinfo->bus_pub.bus->dstats.rx_errors++;
- dev_kfree_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
return;
}
@@ -552,13 +552,15 @@ static void brcmf_usb_rx_complete(struct urb *urb)
if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
brcmf_dbg(ERROR, "rx protocol error\n");
brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
devinfo->bus_pub.bus->dstats.rx_errors++;
} else {
brcmf_rx_packet(devinfo->dev, ifidx, skb);
brcmf_usb_rx_refill(devinfo, req);
}
} else {
- dev_kfree_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
}
return;
@@ -583,14 +585,13 @@ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
req);
- req->urb->transfer_flags |= URB_ZERO_PACKET;
req->devinfo = devinfo;
+ brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (ret == 0) {
- brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
- } else {
- dev_kfree_skb(req->skb);
+ if (ret) {
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
}
@@ -685,23 +686,22 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq);
if (!req) {
+ brcmu_pkt_buf_free_skb(skb);
brcmf_dbg(ERROR, "no req to send\n");
return -ENOMEM;
}
- if (!req->urb) {
- brcmf_dbg(ERROR, "no urb for req %p\n", req);
- return -ENOBUFS;
- }
req->skb = skb;
req->devinfo = devinfo;
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
skb->data, skb->len, brcmf_usb_tx_complete, req);
req->urb->transfer_flags |= URB_ZERO_PACKET;
+ brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (!ret) {
- brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
- } else {
+ if (ret) {
+ brcmf_dbg(ERROR, "brcmf_usb_tx usb_submit_urb FAILED\n");
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index c7230f531e49..a6b382bfabd6 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -1877,16 +1877,17 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
}
if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
- scb_val.val = cpu_to_le32(0);
+ memset(&scb_val, 0, sizeof(scb_val));
err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
sizeof(struct brcmf_scb_val_le));
- if (err)
+ if (err) {
WL_ERR("Could not get rssi (%d)\n", err);
-
- rssi = le32_to_cpu(scb_val.val);
- sinfo->filled |= STATION_INFO_SIGNAL;
- sinfo->signal = rssi;
- WL_CONN("RSSI %d dBm\n", rssi);
+ } else {
+ rssi = le32_to_cpu(scb_val.val);
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = rssi;
+ WL_CONN("RSSI %d dBm\n", rssi);
+ }
}
done:
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index 9a4c63f927cb..7ed7d7577024 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -382,9 +382,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
{
struct brcms_c_info *wlc = wlc_cm->wlc;
struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel;
- const struct ieee80211_reg_rule *reg_rule;
struct txpwr_limits txpwr;
- int ret;
brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
@@ -393,8 +391,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
);
/* set or restore gmode as required by regulatory */
- ret = freq_reg_info(wlc->wiphy, ch->center_freq, 0, &reg_rule);
- if (!ret && (reg_rule->flags & NL80211_RRF_NO_OFDM))
+ if (ch->flags & IEEE80211_CHAN_NO_OFDM)
brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
else
brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 04ecf03fc8cb..718da8d6d658 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -123,7 +123,8 @@ static struct ieee80211_channel brcms_2ghz_chantable[] = {
IEEE80211_CHAN_NO_HT40PLUS),
CHAN2GHZ(14, 2484,
IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+ IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
+ IEEE80211_CHAN_NO_OFDM)
};
static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
@@ -1236,6 +1237,9 @@ uint brcms_reset(struct brcms_info *wl)
/* dpc will not be rescheduled */
wl->resched = false;
+ /* inform publicly that interface is down */
+ wl->pub->up = false;
+
return 0;
}
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index c3e14b2104ef..4d30cd18c3b0 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -2042,7 +2042,8 @@ static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
return;
}
len = ETH_ALEN;
- ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid, &len);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid,
+ &len);
if (ret) {
IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
__LINE__);
diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
index ce826bc5f111..1a98fa3ab06d 100644
--- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
@@ -124,6 +124,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
const struct fw_img *img;
size_t bufsz;
+ if (!iwl_is_ready_rf(priv))
+ return -EAGAIN;
+
/* default is to dump the entire data segment */
if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
priv->dbgfs_sram_offset = 0x800000;
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 6fddd2785e6e..a82f46c10f5e 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -707,11 +707,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
*/
static bool rs_use_green(struct ieee80211_sta *sta)
{
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
- !(ctx->ht.non_gf_sta_present);
+ /*
+ * There's a bug somewhere in this code that causes the
+ * scaling to get stuck because GF+SGI can't be combined
+ * in SISO rates. Until we find that bug, disable GF, it
+ * has only limited benefit and we still interoperate with
+ * GF APs since we can always receive GF transmissions.
+ */
+ return false;
}
/**
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 3ef8d5adc991..71c79943e633 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -351,7 +351,7 @@ int iwl_queue_space(const struct iwl_queue *q);
/*****************************************************
* Error handling
******************************************************/
-int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display);
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
void iwl_dump_csr(struct iwl_trans *trans);
/*****************************************************
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index d80604a2bb1a..498372008810 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -565,7 +565,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans)
}
iwl_dump_csr(trans);
- iwl_dump_fh(trans, NULL, false);
+ iwl_dump_fh(trans, NULL);
iwl_op_mode_nic_error(trans->op_mode);
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 38f51b04217e..848851177e7e 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -1649,13 +1649,9 @@ static const char *get_fh_string(int cmd)
#undef IWL_CMD
}
-int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display)
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
{
int i;
-#ifdef CONFIG_IWLWIFI_DEBUG
- int pos = 0;
- size_t bufsz = 0;
-#endif
static const u32 fh_tbl[] = {
FH_RSCSR_CHNL0_STTS_WPTR_REG,
FH_RSCSR_CHNL0_RBDCB_BASE_REG,
@@ -1667,29 +1663,35 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display)
FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_ERROR_REG
};
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (display) {
- bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (buf) {
+ int pos = 0;
+ size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
*buf = kmalloc(bufsz, GFP_KERNEL);
if (!*buf)
return -ENOMEM;
+
pos += scnprintf(*buf + pos, bufsz - pos,
"FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
pos += scnprintf(*buf + pos, bufsz - pos,
" %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
- }
+
return pos;
}
#endif
+
IWL_ERR(trans, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
IWL_ERR(trans, " %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
- }
+
return 0;
}
@@ -1982,11 +1984,11 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
size_t count, loff_t *ppos)
{
struct iwl_trans *trans = file->private_data;
- char *buf;
+ char *buf = NULL;
int pos = 0;
ssize_t ret = -EFAULT;
- ret = pos = iwl_dump_fh(trans, &buf, true);
+ ret = pos = iwl_dump_fh(trans, &buf);
if (buf) {
ret = simple_read_from_buffer(user_buf,
count, ppos, buf, pos);
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index eb5de800ed90..1c10b542ab23 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -1254,6 +1254,7 @@ static int lbs_associate(struct lbs_private *priv,
netif_tx_wake_all_queues(priv->dev);
}
+ kfree(cmd);
done:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret;
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 76caebaa4397..4cb234349fbf 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -1314,6 +1314,7 @@ static void if_sdio_remove(struct sdio_func *func)
kfree(packet);
}
+ kfree(card);
lbs_deb_leave(LBS_DEB_SDIO);
}
@@ -1325,6 +1326,11 @@ static int if_sdio_suspend(struct device *dev)
mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
+ /* If we're powered off anyway, just let the mmc layer remove the
+ * card. */
+ if (!lbs_iface_active(card->priv))
+ return -ENOSYS;
+
dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
sdio_func_id(func), flags);
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 58048189bd24..fe1ea43c5149 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -571,7 +571,10 @@ static int lbs_thread(void *data)
netdev_info(dev, "Timeout submitting command 0x%04x\n",
le16_to_cpu(cmdnode->cmdbuf->command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
- if (priv->reset_card)
+
+ /* Reset card, but only when it isn't in the process
+ * of being shutdown anyway. */
+ if (!dev->dismantle && priv->reset_card)
priv->reset_card(priv);
}
priv->cmd_timed_out = 0;
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index c229dddcf1c2..225c1a4feeba 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -170,7 +170,20 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
cmd_code = le16_to_cpu(host_cmd->command);
cmd_size = le16_to_cpu(host_cmd->size);
- skb_trim(cmd_node->cmd_skb, cmd_size);
+ /* Adjust skb length */
+ if (cmd_node->cmd_skb->len > cmd_size)
+ /*
+ * cmd_size is less than sizeof(struct host_cmd_ds_command).
+ * Trim off the unused portion.
+ */
+ skb_trim(cmd_node->cmd_skb, cmd_size);
+ else if (cmd_node->cmd_skb->len < cmd_size)
+ /*
+ * cmd_size is larger than sizeof(struct host_cmd_ds_command)
+ * because we have appended custom IE TLV. Increase skb length
+ * accordingly.
+ */
+ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
do_gettimeofday(&tstamp);
dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 241162e8111d..7a4ae9ee1c63 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1803,6 +1803,7 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev,
struct cfg80211_pmksa *pmksa,
int max_pmkids)
{
+ struct ndis_80211_pmkid *new_pmkids;
int i, err, newlen;
unsigned int count;
@@ -1833,11 +1834,12 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev,
/* add new pmkid */
newlen = sizeof(*pmkids) + (count + 1) * sizeof(pmkids->bssid_info[0]);
- pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
- if (!pmkids) {
+ new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
+ if (!new_pmkids) {
err = -ENOMEM;
goto error;
}
+ pmkids = new_pmkids;
pmkids->length = cpu_to_le32(newlen);
pmkids->bssid_info_count = cpu_to_le32(count + 1);
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 8b9dbd76a252..64328af496f5 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1611,6 +1611,7 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1624,6 +1625,14 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+ rt2x00_set_field32(&reg, GPIOCSR_BIT8, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2400pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h
index d3a4a68cc439..7564ae992b73 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/rt2x00/rt2400pci.h
@@ -670,6 +670,7 @@
#define GPIOCSR_BIT5 FIELD32(0x00000020)
#define GPIOCSR_BIT6 FIELD32(0x00000040)
#define GPIOCSR_BIT7 FIELD32(0x00000080)
+#define GPIOCSR_BIT8 FIELD32(0x00000100)
/*
* BBPPCSR: BBP Pin control register.
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index d2cf8a4bc8b5..3de0406735f6 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1929,6 +1929,7 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1942,6 +1943,14 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+ rt2x00_set_field32(&reg, GPIOCSR_DIR0, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2500pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 3aae36bb0a9e..89fee311d8fd 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -283,7 +283,7 @@ static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
u16 reg;
rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
- return rt2x00_get_field32(reg, MAC_CSR19_BIT7);
+ return rt2x00_get_field16(reg, MAC_CSR19_BIT7);
}
#ifdef CONFIG_RT2X00_LIB_LEDS
@@ -1768,6 +1768,7 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u16 reg;
/*
* Allocate eeprom data.
@@ -1781,6 +1782,14 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR19_BIT8, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2500usb_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h
index b493306a7eed..196bd5103e4f 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/rt2x00/rt2500usb.h
@@ -189,14 +189,15 @@
* MAC_CSR19: GPIO control register.
*/
#define MAC_CSR19 0x0426
-#define MAC_CSR19_BIT0 FIELD32(0x0001)
-#define MAC_CSR19_BIT1 FIELD32(0x0002)
-#define MAC_CSR19_BIT2 FIELD32(0x0004)
-#define MAC_CSR19_BIT3 FIELD32(0x0008)
-#define MAC_CSR19_BIT4 FIELD32(0x0010)
-#define MAC_CSR19_BIT5 FIELD32(0x0020)
-#define MAC_CSR19_BIT6 FIELD32(0x0040)
-#define MAC_CSR19_BIT7 FIELD32(0x0080)
+#define MAC_CSR19_BIT0 FIELD16(0x0001)
+#define MAC_CSR19_BIT1 FIELD16(0x0002)
+#define MAC_CSR19_BIT2 FIELD16(0x0004)
+#define MAC_CSR19_BIT3 FIELD16(0x0008)
+#define MAC_CSR19_BIT4 FIELD16(0x0010)
+#define MAC_CSR19_BIT5 FIELD16(0x0020)
+#define MAC_CSR19_BIT6 FIELD16(0x0040)
+#define MAC_CSR19_BIT7 FIELD16(0x0080)
+#define MAC_CSR19_BIT8 FIELD16(0x0100)
/*
* MAC_CSR20: LED control register.
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 88455b1b9fe0..b93516d832fb 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -221,6 +221,67 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
mutex_unlock(&rt2x00dev->csr_mutex);
}
+static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ int i, count;
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+ if (rt2x00_get_field32(reg, WLAN_EN))
+ return 0;
+
+ rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
+ rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
+ rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
+ rt2x00_set_field32(&reg, WLAN_EN, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+
+ udelay(REGISTER_BUSY_DELAY);
+
+ count = 0;
+ do {
+ /*
+ * Check PLL_LD & XTAL_RDY.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
+ if (rt2x00_get_field32(reg, PLL_LD) &&
+ rt2x00_get_field32(reg, XTAL_RDY))
+ break;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ if (i >= REGISTER_BUSY_COUNT) {
+
+ if (count >= 10)
+ return -EIO;
+
+ rt2800_register_write(rt2x00dev, 0x58, 0x018);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x418);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x618);
+ udelay(REGISTER_BUSY_DELAY);
+ count++;
+ } else {
+ count = 0;
+ }
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+ rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
+ rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
+ rt2x00_set_field32(&reg, WLAN_RESET, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2x00_set_field32(&reg, WLAN_RESET, 0);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
+ } while (count != 0);
+
+ return 0;
+}
+
void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token,
const u8 arg0, const u8 arg1)
@@ -400,6 +461,13 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
{
unsigned int i;
u32 reg;
+ int retval;
+
+ if (rt2x00_rt(rt2x00dev, RT3290)) {
+ retval = rt2800_enable_wlan_rt3290(rt2x00dev);
+ if (retval)
+ return -EBUSY;
+ }
/*
* If driver doesn't wake up firmware here,
@@ -4021,6 +4089,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
msleep(1);
rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+ rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 0);
rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
}
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 235376e9cb04..4765bbd654cd 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -980,69 +980,10 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
return rt2800_validate_eeprom(rt2x00dev);
}
-static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- int i, count;
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
- if (rt2x00_get_field32(reg, WLAN_EN))
- return 0;
-
- rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
- rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
- rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
- rt2x00_set_field32(&reg, WLAN_EN, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-
- udelay(REGISTER_BUSY_DELAY);
-
- count = 0;
- do {
- /*
- * Check PLL_LD & XTAL_RDY.
- */
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
- if (rt2x00_get_field32(reg, PLL_LD) &&
- rt2x00_get_field32(reg, XTAL_RDY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- if (i >= REGISTER_BUSY_COUNT) {
-
- if (count >= 10)
- return -EIO;
-
- rt2800_register_write(rt2x00dev, 0x58, 0x018);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x418);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x618);
- udelay(REGISTER_BUSY_DELAY);
- count++;
- } else {
- count = 0;
- }
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
- rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
- rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
- rt2x00_set_field32(&reg, WLAN_RESET, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2x00_set_field32(&reg, WLAN_RESET, 0);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
- } while (count != 0);
-
- return 0;
-}
static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1056,6 +997,14 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2800_probe_hw_mode(rt2x00dev);
@@ -1063,17 +1012,6 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
- * In probe phase call rt2800_enable_wlan_rt3290 to enable wlan
- * clk for rt3290. That avoid the MCU fail in start phase.
- */
- if (rt2x00_rt(rt2x00dev, RT3290)) {
- retval = rt2800_enable_wlan_rt3290(rt2x00dev);
-
- if (retval)
- return retval;
- }
-
- /*
* This device has multiple filters for control frames
* and has a separate filter for PS Poll frames.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 6cf336595e25..6b4226b71618 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -667,8 +667,16 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
skb_pull(entry->skb, RXINFO_DESC_SIZE);
/*
- * FIXME: we need to check for rx_pkt_len validity
+ * Check for rx_pkt_len validity. Return if invalid, leaving
+ * rxdesc->size zeroed out by the upper level.
*/
+ if (unlikely(rx_pkt_len == 0 ||
+ rx_pkt_len > entry->queue->data_size)) {
+ ERROR(entry->queue->rt2x00dev,
+ "Bad frame size %d, forcing to 0\n", rx_pkt_len);
+ return;
+ }
+
rxd = (__le32 *)(entry->skb->data + rx_pkt_len);
/*
@@ -736,6 +744,7 @@ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -749,6 +758,14 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+ rt2x00usb_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2800_probe_hw_mode(rt2x00dev);
@@ -1157,6 +1174,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x1690, 0x0744) },
{ USB_DEVICE(0x1690, 0x0761) },
{ USB_DEVICE(0x1690, 0x0764) },
+ /* ASUS */
+ { USB_DEVICE(0x0b05, 0x179d) },
/* Cisco */
{ USB_DEVICE(0x167b, 0x4001) },
/* EnGenius */
@@ -1222,7 +1241,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0b05, 0x1760) },
{ USB_DEVICE(0x0b05, 0x1761) },
{ USB_DEVICE(0x0b05, 0x1790) },
- { USB_DEVICE(0x0b05, 0x179d) },
/* AzureWave */
{ USB_DEVICE(0x13d3, 0x3262) },
{ USB_DEVICE(0x13d3, 0x3284) },
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index a59048ffa092..10cf67267775 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -629,7 +629,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
*/
if (unlikely(rxdesc.size == 0 ||
rxdesc.size > entry->queue->data_size)) {
- WARNING(rt2x00dev, "Wrong frame size %d max %d.\n",
+ ERROR(rt2x00dev, "Wrong frame size %d max %d.\n",
rxdesc.size, entry->queue->data_size);
dev_kfree_skb(entry->skb);
goto renew_skb;
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index f32259686b45..b8ec96163922 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2243,8 +2243,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)
{
- struct ieee80211_conf conf = { .flags = 0 };
- struct rt2x00lib_conf libconf = { .conf = &conf };
+ struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf };
rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
}
@@ -2833,6 +2832,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Disable power saving.
@@ -2851,6 +2851,14 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, MAC_CSR13, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR13_BIT13, 1);
+ rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt61pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h
index e3cd6db76b0e..8f3da5a56766 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/rt2x00/rt61pci.h
@@ -372,6 +372,7 @@ struct hw_pairwise_ta_entry {
#define MAC_CSR13_BIT10 FIELD32(0x00000400)
#define MAC_CSR13_BIT11 FIELD32(0x00000800)
#define MAC_CSR13_BIT12 FIELD32(0x00001000)
+#define MAC_CSR13_BIT13 FIELD32(0x00002000)
/*
* MAC_CSR14: LED control register.
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index ba6e434b859d..248436c13ce0 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2177,6 +2177,7 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -2190,6 +2191,14 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR13, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR13_BIT15, 0);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt73usb_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h
index 9f6b470414d3..df1cc116b83b 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/rt2x00/rt73usb.h
@@ -282,6 +282,9 @@ struct hw_pairwise_ta_entry {
#define MAC_CSR13_BIT10 FIELD32(0x00000400)
#define MAC_CSR13_BIT11 FIELD32(0x00000800)
#define MAC_CSR13_BIT12 FIELD32(0x00001000)
+#define MAC_CSR13_BIT13 FIELD32(0x00002000)
+#define MAC_CSR13_BIT14 FIELD32(0x00004000)
+#define MAC_CSR13_BIT15 FIELD32(0x00008000)
/*
* MAC_CSR14: LED control register.
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index c8f40c9c0428..3782e1cd3697 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -95,6 +95,7 @@ MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
enum acer_wmi_event_ids {
WMID_HOTKEY_EVENT = 0x1,
+ WMID_ACCEL_EVENT = 0x5,
};
static const struct key_entry acer_wmi_keymap[] = {
@@ -130,6 +131,7 @@ static const struct key_entry acer_wmi_keymap[] = {
};
static struct input_dev *acer_wmi_input_dev;
+static struct input_dev *acer_wmi_accel_dev;
struct event_return_value {
u8 function;
@@ -200,6 +202,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_BLUETOOTH (1<<2)
#define ACER_CAP_BRIGHTNESS (1<<3)
#define ACER_CAP_THREEG (1<<4)
+#define ACER_CAP_ACCEL (1<<5)
#define ACER_CAP_ANY (0xFFFFFFFF)
/*
@@ -1399,6 +1402,60 @@ static void acer_backlight_exit(void)
}
/*
+ * Accelerometer device
+ */
+static acpi_handle gsensor_handle;
+
+static int acer_gsensor_init(void)
+{
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+ status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ return 0;
+}
+
+static int acer_gsensor_open(struct input_dev *input)
+{
+ return acer_gsensor_init();
+}
+
+static int acer_gsensor_event(void)
+{
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object out_obj[5];
+
+ if (!has_cap(ACER_CAP_ACCEL))
+ return -1;
+
+ output.length = sizeof(out_obj);
+ output.pointer = out_obj;
+
+ status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ if (out_obj->package.count != 4)
+ return -1;
+
+ input_report_abs(acer_wmi_accel_dev, ABS_X,
+ (s16)out_obj->package.elements[0].integer.value);
+ input_report_abs(acer_wmi_accel_dev, ABS_Y,
+ (s16)out_obj->package.elements[1].integer.value);
+ input_report_abs(acer_wmi_accel_dev, ABS_Z,
+ (s16)out_obj->package.elements[2].integer.value);
+ input_sync(acer_wmi_accel_dev);
+ return 0;
+}
+
+/*
* Rfkill devices
*/
static void acer_rfkill_update(struct work_struct *ignored);
@@ -1673,6 +1730,9 @@ static void acer_wmi_notify(u32 value, void *context)
1, true);
}
break;
+ case WMID_ACCEL_EVENT:
+ acer_gsensor_event();
+ break;
default:
pr_warn("Unknown function number - %d - %d\n",
return_value.function, return_value.key_num);
@@ -1758,6 +1818,73 @@ static int acer_wmi_enable_lm(void)
return status;
}
+static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
+ void *ctx, void **retval)
+{
+ *(acpi_handle *)retval = ah;
+ return AE_OK;
+}
+
+static int __init acer_wmi_get_handle(const char *name, const char *prop,
+ acpi_handle *ah)
+{
+ acpi_status status;
+ acpi_handle handle;
+
+ BUG_ON(!name || !ah);
+
+ handle = NULL;
+ status = acpi_get_devices(prop, acer_wmi_get_handle_cb,
+ (void *)name, &handle);
+
+ if (ACPI_SUCCESS(status)) {
+ *ah = handle;
+ return 0;
+ } else {
+ return -ENODEV;
+ }
+}
+
+static int __init acer_wmi_accel_setup(void)
+{
+ int err;
+
+ err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+ if (err)
+ return err;
+
+ interface->capability |= ACER_CAP_ACCEL;
+
+ acer_wmi_accel_dev = input_allocate_device();
+ if (!acer_wmi_accel_dev)
+ return -ENOMEM;
+
+ acer_wmi_accel_dev->open = acer_gsensor_open;
+
+ acer_wmi_accel_dev->name = "Acer BMA150 accelerometer";
+ acer_wmi_accel_dev->phys = "wmi/input1";
+ acer_wmi_accel_dev->id.bustype = BUS_HOST;
+ acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0);
+
+ err = input_register_device(acer_wmi_accel_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+err_free_dev:
+ input_free_device(acer_wmi_accel_dev);
+ return err;
+}
+
+static void acer_wmi_accel_destroy(void)
+{
+ input_unregister_device(acer_wmi_accel_dev);
+}
+
static int __init acer_wmi_input_setup(void)
{
acpi_status status;
@@ -1912,6 +2039,9 @@ static int acer_resume(struct device *dev)
if (has_cap(ACER_CAP_BRIGHTNESS))
set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_gsensor_init();
+
return 0;
}
@@ -2060,14 +2190,16 @@ static int __init acer_wmi_init(void)
set_quirks();
+ if (dmi_check_system(video_vendor_dmi_table))
+ acpi_video_dmi_promote_vendor();
if (acpi_video_backlight_support()) {
- if (dmi_check_system(video_vendor_dmi_table)) {
- acpi_video_unregister();
- } else {
- interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by "
- "acpi video driver\n");
- }
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by acpi video driver\n");
+ } else {
+#ifdef CONFIG_ACPI_VIDEO
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
+#endif
}
if (wmi_has_guid(WMID_GUID3)) {
@@ -2090,6 +2222,8 @@ static int __init acer_wmi_init(void)
return err;
}
+ acer_wmi_accel_setup();
+
err = platform_driver_register(&acer_platform_driver);
if (err) {
pr_err("Unable to register platform driver\n");
@@ -2133,6 +2267,8 @@ error_device_alloc:
error_platform_register:
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_wmi_accel_destroy();
return err;
}
@@ -2142,6 +2278,9 @@ static void __exit acer_wmi_exit(void)
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_wmi_accel_destroy();
+
remove_sysfs(acer_platform_device);
remove_debugfs();
platform_device_unregister(acer_platform_device);
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 694a15a56230..905fa01ac8df 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -193,7 +193,10 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
* backlight control and supports more levels than other options.
* Disable the other backlight choices.
*/
+ acpi_video_dmi_promote_vendor();
+#ifdef CONFIG_ACPI_VIDEO
acpi_video_unregister();
+#endif
apple_bl_unregister();
return 0;
@@ -213,7 +216,10 @@ static void __devexit gmux_remove(struct pnp_dev *pnp)
release_region(gmux_data->iostart, gmux_data->iolen);
kfree(gmux_data);
+ acpi_video_dmi_demote_vendor();
+#ifdef CONFIG_ACPI_VIDEO
acpi_video_register();
+#endif
apple_bl_register();
}
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 99a30b513137..6b0ebdeae916 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -26,6 +26,7 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/fb.h>
+#include <linux/dmi.h>
#include "asus-wmi.h"
@@ -48,18 +49,115 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
* 1 | Hardware | Software
* 4 | Software | Software
*/
-static uint wapf;
+static int wapf = -1;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static struct quirk_entry *quirks;
+
static struct quirk_entry quirk_asus_unknown = {
+ .wapf = 0,
+};
+
+static struct quirk_entry quirk_asus_x401u = {
+ .wapf = 4,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
+static struct dmi_system_id asus_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X401U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X401A1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X501U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X501A1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55C",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55C"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55VD",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {},
};
static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->quirks = &quirk_asus_unknown;
- driver->quirks->wapf = wapf;
+ quirks = &quirk_asus_unknown;
+ dmi_check_system(asus_quirks);
+
+ driver->quirks = quirks;
driver->panel_power = FB_BLANK_UNBLANK;
+
+ /* overwrite the wapf setting if the wapf paramater is specified */
+ if (wapf != -1)
+ quirks->wapf = wapf;
+ else
+ wapf = quirks->wapf;
}
static const struct key_entry asus_nb_wmi_keymap[] = {
@@ -94,6 +192,10 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x8A, { KEY_PROG1 } },
{ KE_KEY, 0x95, { KEY_MEDIA } },
{ KE_KEY, 0x99, { KEY_PHONE } },
+ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
+ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
+ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
+ { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
{ KE_KEY, 0xb5, { KEY_CALC } },
{ KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
{ KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 77aadde5281c..c7a36f6b0580 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -47,6 +47,9 @@
#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
+#ifdef CONFIG_ACPI_VIDEO
+#include <acpi/video.h>
+#endif
#include "asus-wmi.h"
@@ -136,6 +139,9 @@ MODULE_LICENSE("GPL");
/* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012
+/* Deep S3 / Resume on LID open */
+#define ASUS_WMI_DEVID_LID_RESUME 0x00120031
+
/* DSTS masks */
#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001
#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002
@@ -1365,6 +1371,7 @@ static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
+ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -1390,6 +1397,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_touchpad.attr,
+ &dev_attr_lid_resume.attr,
NULL
};
@@ -1408,6 +1416,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_CARDREADER;
else if (attr == &dev_attr_touchpad.attr)
devid = ASUS_WMI_DEVID_TOUCHPAD;
+ else if (attr == &dev_attr_lid_resume.attr)
+ devid = ASUS_WMI_DEVID_LID_RESUME;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -1467,14 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
*/
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
- else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL))
+ else
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
- if (!asus->dsts_id) {
- pr_err("Can't find DSTS");
- return -ENODEV;
- }
-
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
if (asus->driver->quirks->wapf >= 0)
@@ -1681,7 +1686,13 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_rfkill;
+ if (asus->driver->quirks->wmi_backlight_power)
+ acpi_video_dmi_promote_vendor();
if (!acpi_video_backlight_support()) {
+#ifdef CONFIG_ACPI_VIDEO
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
+#endif
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
goto fail_backlight;
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index d43b66742004..9c1da8b81bea 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -39,6 +39,7 @@ struct quirk_entry {
bool hotplug_wireless;
bool scalar_panel_brightness;
bool store_backlight_power;
+ bool wmi_backlight_power;
int wapf;
};
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index e2230a2b2f8e..2ca7dd1ab3e4 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -31,15 +31,21 @@ MODULE_LICENSE("GPL");
struct cmpc_accel {
int sensitivity;
+ int g_select;
+ int inputdev_state;
};
-#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_DEV_STATE_CLOSED 0
+#define CMPC_ACCEL_DEV_STATE_OPEN 1
+#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
+#define CMPC_ACCEL_G_SELECT_DEFAULT 0
#define CMPC_ACCEL_HID "ACCE0000"
+#define CMPC_ACCEL_HID_V4 "ACCE0001"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_IPML_HID "IPML200"
-#define CMPC_KEYS_HID "FnBT0000"
+#define CMPC_KEYS_HID "FNBT0000"
/*
* Generic input device code.
@@ -76,7 +82,391 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
}
/*
- * Accelerometer code.
+ * Accelerometer code for Classmate V4
+ */
+static acpi_status cmpc_start_accel_v4(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x3;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x4;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
+ return status;
+}
+
+static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x02;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x05;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = val;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ return acpi_evaluate_object(handle, "ACMD", &input, NULL);
+}
+
+static acpi_status cmpc_get_accel_v4(acpi_handle handle,
+ int16_t *x,
+ int16_t *y,
+ int16_t *z)
+{
+ union acpi_object param[4];
+ struct acpi_object_list input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ int16_t *locs;
+ acpi_status status;
+
+ param[0].type = ACPI_TYPE_INTEGER;
+ param[0].integer.value = 0x01;
+ param[1].type = ACPI_TYPE_INTEGER;
+ param[1].integer.value = 0;
+ param[2].type = ACPI_TYPE_INTEGER;
+ param[2].integer.value = 0;
+ param[3].type = ACPI_TYPE_INTEGER;
+ param[3].integer.value = 0;
+ input.count = 4;
+ input.pointer = param;
+ status = acpi_evaluate_object(handle, "ACMD", &input, &output);
+ if (ACPI_SUCCESS(status)) {
+ union acpi_object *obj;
+ obj = output.pointer;
+ locs = (int16_t *) obj->buffer.pointer;
+ *x = locs[0];
+ *y = locs[1];
+ *z = locs[2];
+ kfree(output.pointer);
+ }
+ return status;
+}
+
+static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
+{
+ if (event == 0x81) {
+ int16_t x, y, z;
+ acpi_status status;
+
+ status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
+ if (ACPI_SUCCESS(status)) {
+ struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
+
+ input_report_abs(inputdev, ABS_X, x);
+ input_report_abs(inputdev, ABS_Y, y);
+ input_report_abs(inputdev, ABS_Z, z);
+ input_sync(inputdev);
+ }
+ }
+}
+
+static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->sensitivity);
+}
+
+static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long sensitivity;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &sensitivity);
+ if (r)
+ return r;
+
+ /* sensitivity must be between 1 and 127 */
+ if (sensitivity < 1 || sensitivity > 127)
+ return -EINVAL;
+
+ accel->sensitivity = sensitivity;
+ cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
+ .attr = { .name = "sensitivity", .mode = 0660 },
+ .show = cmpc_accel_sensitivity_show_v4,
+ .store = cmpc_accel_sensitivity_store_v4
+};
+
+static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ return sprintf(buf, "%d\n", accel->g_select);
+}
+
+static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+ unsigned long g_select;
+ int r;
+
+ acpi = to_acpi_device(dev);
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ r = kstrtoul(buf, 0, &g_select);
+ if (r)
+ return r;
+
+ /* 0 means 1.5g, 1 means 6g, everything else is wrong */
+ if (g_select != 0 && g_select != 1)
+ return -EINVAL;
+
+ accel->g_select = g_select;
+ cmpc_accel_set_g_select_v4(acpi->handle, g_select);
+
+ return strnlen(buf, count);
+}
+
+static struct device_attribute cmpc_accel_g_select_attr_v4 = {
+ .attr = { .name = "g_select", .mode = 0660 },
+ .show = cmpc_accel_g_select_show_v4,
+ .store = cmpc_accel_g_select_store_v4
+};
+
+static int cmpc_accel_open_v4(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+ cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+ if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
+ return 0;
+ }
+ return -EIO;
+}
+
+static void cmpc_accel_close_v4(struct input_dev *input)
+{
+ struct acpi_device *acpi;
+ struct cmpc_accel *accel;
+
+ acpi = to_acpi_device(input->dev.parent);
+ accel = dev_get_drvdata(&input->dev);
+
+ cmpc_stop_accel_v4(acpi->handle);
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+}
+
+static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
+{
+ set_bit(EV_ABS, inputdev->evbit);
+ input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
+ input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
+ inputdev->open = cmpc_accel_open_v4;
+ inputdev->close = cmpc_accel_close_v4;
+}
+
+static int cmpc_accel_suspend_v4(struct device *dev)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
+ return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
+
+ return 0;
+}
+
+static int cmpc_accel_resume_v4(struct device *dev)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
+ cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
+ accel->sensitivity);
+ cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
+ accel->g_select);
+
+ if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cmpc_accel_add_v4(struct acpi_device *acpi)
+{
+ int error;
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ accel = kmalloc(sizeof(*accel), GFP_KERNEL);
+ if (!accel)
+ return -ENOMEM;
+
+ accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
+
+ accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
+ cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+ if (error)
+ goto failed_sensitivity;
+
+ accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
+ cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
+
+ error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+ if (error)
+ goto failed_g_select;
+
+ error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
+ cmpc_accel_idev_init_v4);
+ if (error)
+ goto failed_input;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ dev_set_drvdata(&inputdev->dev, accel);
+
+ return 0;
+
+failed_input:
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+failed_g_select:
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+failed_sensitivity:
+ kfree(accel);
+ return error;
+}
+
+static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
+ device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
+ return cmpc_remove_acpi_notify_device(acpi);
+}
+
+static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
+ cmpc_accel_resume_v4);
+
+static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
+ {CMPC_ACCEL_HID_V4, 0},
+ {"", 0}
+};
+
+static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
+ .owner = THIS_MODULE,
+ .name = "cmpc_accel_v4",
+ .class = "cmpc_accel_v4",
+ .ids = cmpc_accel_device_ids_v4,
+ .ops = {
+ .add = cmpc_accel_add_v4,
+ .remove = cmpc_accel_remove_v4,
+ .notify = cmpc_accel_handler_v4,
+ },
+ .drv.pm = &cmpc_accel_pm,
+};
+
+
+/*
+ * Accelerometer code for Classmate versions prior to V4
*/
static acpi_status cmpc_start_accel(acpi_handle handle)
{
@@ -726,8 +1116,15 @@ static int cmpc_init(void)
if (r)
goto failed_accel;
+ r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
+ if (r)
+ goto failed_accel_v4;
+
return r;
+failed_accel_v4:
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
+
failed_accel:
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
@@ -743,6 +1140,7 @@ failed_keys:
static void cmpc_exit(void)
{
+ acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
@@ -754,6 +1152,7 @@ module_exit(cmpc_exit);
static const struct acpi_device_id cmpc_device_ids[] = {
{CMPC_ACCEL_HID, 0},
+ {CMPC_ACCEL_HID_V4, 0},
{CMPC_TABLET_HID, 0},
{CMPC_IPML_HID, 0},
{CMPC_KEYS_HID, 0},
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 5f78aac9b163..4e96e8c0b60f 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -206,6 +206,60 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
},
.driver_data = &quirk_dell_vostro_v130,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5720",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7720",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
{ }
};
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 656761380342..5838332ea5bd 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -79,7 +79,7 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
{ KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
{ KE_KEY, 0xe8, { KEY_SCREENLOCK } },
- { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
+ { KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } },
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
@@ -107,6 +107,11 @@ static struct quirk_entry quirk_asus_et2012_type3 = {
.store_backlight_power = true,
};
+static struct quirk_entry quirk_asus_x101ch = {
+ /* We need this when ACPI function doesn't do this well */
+ .wmi_backlight_power = true,
+};
+
static struct quirk_entry *quirks;
static void et2012_quirks(void)
@@ -157,6 +162,24 @@ static struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_unknown,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. X101CH",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
+ },
+ .driver_data = &quirk_asus_x101ch,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. 1015CX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
+ },
+ .driver_data = &quirk_asus_x101ch,
+ },
{},
};
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index e2a34b42ddc1..c1ca7bcebb66 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -26,7 +26,7 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#ifdef CONFIG_ACPI_VIDEO
#include <acpi/video.h>
#endif
@@ -1465,6 +1465,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
},
},
+ /* DMI ids for laptops with bad Chassis Type */
+ {
+ .ident = "R40/R41",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"),
+ DMI_MATCH(DMI_BOARD_NAME, "R40/R41"),
+ },
+ },
/* Specific DMI ids for laptop with quirks */
{
.callback = samsung_dmi_matched,
@@ -1506,6 +1515,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
},
.driver_data = &samsung_broken_acpi_video,
},
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "X360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
+ DMI_MATCH(DMI_BOARD_NAME, "X360"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
{ },
};
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -1530,15 +1549,18 @@ static int __init samsung_init(void)
samsung->quirks = quirks;
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#ifdef CONFIG_ACPI
+ if (samsung->quirks->broken_acpi_video)
+ acpi_video_dmi_promote_vendor();
+
/* Don't handle backlight here if the acpi video already handle it */
if (acpi_video_backlight_support()) {
- if (samsung->quirks->broken_acpi_video) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
- } else {
- samsung->handle_backlight = false;
- }
+ samsung->handle_backlight = false;
+ } else if (samsung->quirks->broken_acpi_video) {
+ pr_info("Disabling ACPI video driver\n");
+#ifdef CONFIG_ACPI_VIDEO
+ acpi_video_unregister();
+#endif
}
#endif
@@ -1552,8 +1574,7 @@ static int __init samsung_init(void)
#ifdef CONFIG_ACPI
/* Only log that if we are really on a sabi platform */
- if (acpi_video_backlight_support() &&
- !samsung->quirks->broken_acpi_video)
+ if (acpi_video_backlight_support())
pr_info("Backlight controlled by ACPI video driver\n");
#endif
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index d5fd4a1193f8..e7f73287636c 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -3015,8 +3015,6 @@ static void hotkey_exit(void)
if (hotkey_dev_attributes)
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
- kfree(hotkey_keycode_map);
-
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
"restoring original HKEY status and mask\n");
/* yes, there is a bitwise or below, we want the
@@ -5217,6 +5215,7 @@ static void led_exit(void)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
+ flush_workqueue(tpacpi_wq);
kfree(tpacpi_leds);
}
@@ -8936,6 +8935,7 @@ static void thinkpad_acpi_module_exit(void)
input_unregister_device(tpacpi_inputdev);
else
input_free_device(tpacpi_inputdev);
+ kfree(hotkey_keycode_map);
}
if (tpacpi_hwmon)
@@ -8969,6 +8969,7 @@ static void thinkpad_acpi_module_exit(void)
kfree(thinkpad_id.bios_version_str);
kfree(thinkpad_id.ec_version_str);
kfree(thinkpad_id.model_str);
+ kfree(thinkpad_id.nummodel_str);
}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index aa764ecc4e60..c1892f321c46 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -268,6 +268,7 @@ config CHARGER_GPIO
config CHARGER_MANAGER
bool "Battery charger manager for multiple chargers"
depends on REGULATOR && RTC_CLASS
+ select EXTCON
help
Say Y to enable charger-manager support, which allows multiple
chargers attached to a battery and multiple batteries attached to a
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index f5d6d379f2fb..181ddece5181 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -22,6 +22,7 @@
* Datasheets:
* http://focus.ti.com/docs/prod/folders/print/bq27000.html
* http://focus.ti.com/docs/prod/folders/print/bq27500.html
+ * http://www.ti.com/product/bq27425-g1
*/
#include <linux/module.h>
@@ -51,6 +52,7 @@
#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */
#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */
#define BQ27x00_REG_AE 0x22 /* Available energy */
+#define BQ27x00_POWER_AVG 0x24
#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */
#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */
@@ -66,15 +68,21 @@
#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */
#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */
#define BQ27500_FLAG_FC BIT(9)
+#define BQ27500_FLAG_OTC BIT(15)
+
+/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
+#define BQ27425_REG_OFFSET 0x04
+#define BQ27425_REG_SOC 0x18 /* Register address plus offset */
#define BQ27000_RS 20 /* Resistor sense */
+#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000)
struct bq27x00_device_info;
struct bq27x00_access_methods {
int (*read)(struct bq27x00_device_info *di, u8 reg, bool single);
};
-enum bq27x00_chip { BQ27000, BQ27500 };
+enum bq27x00_chip { BQ27000, BQ27500, BQ27425};
struct bq27x00_reg_cache {
int temperature;
@@ -86,6 +94,8 @@ struct bq27x00_reg_cache {
int capacity;
int energy;
int flags;
+ int power_avg;
+ int health;
};
struct bq27x00_device_info {
@@ -123,6 +133,22 @@ static enum power_supply_property bq27x00_battery_props[] = {
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_POWER_AVG,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+static enum power_supply_property bq27425_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
static unsigned int poll_interval = 360;
@@ -137,10 +163,24 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \
static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
bool single)
{
+ if (di->chip == BQ27425)
+ return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
return di->bus.read(di, reg, single);
}
/*
+ * Higher versions of the chip like BQ27425 and BQ27500
+ * differ from BQ27000 and BQ27200 in calculation of certain
+ * parameters. Hence we need to check for the chip type.
+ */
+static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di)
+{
+ if (di->chip == BQ27425 || di->chip == BQ27500)
+ return true;
+ return false;
+}
+
+/*
* Return the battery Relative State-of-Charge
* Or < 0 if something fails.
*/
@@ -150,6 +190,8 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
if (di->chip == BQ27500)
rsoc = bq27x00_read(di, BQ27500_REG_SOC, false);
+ else if (di->chip == BQ27425)
+ rsoc = bq27x00_read(di, BQ27425_REG_SOC, false);
else
rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
@@ -174,7 +216,7 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
return charge;
}
- if (di->chip == BQ27500)
+ if (bq27xxx_is_chip_version_higher(di))
charge *= 1000;
else
charge = charge * 3570 / BQ27000_RS;
@@ -208,7 +250,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
{
int ilmd;
- if (di->chip == BQ27500)
+ if (bq27xxx_is_chip_version_higher(di))
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
else
ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
@@ -218,7 +260,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
return ilmd;
}
- if (di->chip == BQ27500)
+ if (bq27xxx_is_chip_version_higher(di))
ilmd *= 1000;
else
ilmd = ilmd * 256 * 3570 / BQ27000_RS;
@@ -262,7 +304,7 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
return temp;
}
- if (di->chip == BQ27500)
+ if (bq27xxx_is_chip_version_higher(di))
temp -= 2731;
else
temp = ((temp * 5) - 5463) / 2;
@@ -306,14 +348,70 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
return tval * 60;
}
+/*
+ * Read a power avg register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)
+{
+ int tval;
+
+ tval = bq27x00_read(di, reg, false);
+ if (tval < 0) {
+ dev_err(di->dev, "error reading power avg rgister %02x: %d\n",
+ reg, tval);
+ return tval;
+ }
+
+ if (di->chip == BQ27500)
+ return tval;
+ else
+ return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS;
+}
+
+/*
+ * Read flag register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
+{
+ int tval;
+
+ tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
+ if (tval < 0) {
+ dev_err(di->dev, "error reading flag register:%d\n", tval);
+ return tval;
+ }
+
+ if ((di->chip == BQ27500)) {
+ if (tval & BQ27500_FLAG_SOCF)
+ tval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (tval & BQ27500_FLAG_OTC)
+ tval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ tval = POWER_SUPPLY_HEALTH_GOOD;
+ return tval;
+ } else {
+ if (tval & BQ27000_FLAG_EDV1)
+ tval = POWER_SUPPLY_HEALTH_DEAD;
+ else
+ tval = POWER_SUPPLY_HEALTH_GOOD;
+ return tval;
+ }
+
+ return -1;
+}
+
static void bq27x00_update(struct bq27x00_device_info *di)
{
struct bq27x00_reg_cache cache = {0, };
bool is_bq27500 = di->chip == BQ27500;
+ bool is_bq27425 = di->chip == BQ27425;
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
if (cache.flags >= 0) {
- if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) {
+ if (!is_bq27500 && !is_bq27425
+ && (cache.flags & BQ27000_FLAG_CI)) {
dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
cache.capacity = -ENODATA;
cache.energy = -ENODATA;
@@ -321,16 +419,30 @@ static void bq27x00_update(struct bq27x00_device_info *di)
cache.time_to_empty_avg = -ENODATA;
cache.time_to_full = -ENODATA;
cache.charge_full = -ENODATA;
+ cache.health = -ENODATA;
} else {
cache.capacity = bq27x00_battery_read_rsoc(di);
- cache.energy = bq27x00_battery_read_energy(di);
- cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);
- cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
- cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
+ if (!is_bq27425) {
+ cache.energy = bq27x00_battery_read_energy(di);
+ cache.time_to_empty =
+ bq27x00_battery_read_time(di,
+ BQ27x00_REG_TTE);
+ cache.time_to_empty_avg =
+ bq27x00_battery_read_time(di,
+ BQ27x00_REG_TTECP);
+ cache.time_to_full =
+ bq27x00_battery_read_time(di,
+ BQ27x00_REG_TTF);
+ }
cache.charge_full = bq27x00_battery_read_lmd(di);
+ cache.health = bq27x00_battery_read_health(di);
}
cache.temperature = bq27x00_battery_read_temperature(di);
+ if (!is_bq27425)
+ cache.cycle_count = bq27x00_battery_read_cyct(di);
cache.cycle_count = bq27x00_battery_read_cyct(di);
+ cache.power_avg =
+ bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
/* We only have to read charge design full once */
if (di->charge_design_full <= 0)
@@ -376,7 +488,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,
return curr;
}
- if (di->chip == BQ27500) {
+ if (bq27xxx_is_chip_version_higher(di)) {
/* bq27500 returns signed value */
val->intval = (int)((s16)curr) * 1000;
} else {
@@ -397,7 +509,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
{
int status;
- if (di->chip == BQ27500) {
+ if (bq27xxx_is_chip_version_higher(di)) {
if (di->cache.flags & BQ27500_FLAG_FC)
status = POWER_SUPPLY_STATUS_FULL;
else if (di->cache.flags & BQ27500_FLAG_DSC)
@@ -425,7 +537,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
{
int level;
- if (di->chip == BQ27500) {
+ if (bq27xxx_is_chip_version_higher(di)) {
if (di->cache.flags & BQ27500_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27500_FLAG_SOC1)
@@ -550,6 +662,12 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_ENERGY_NOW:
ret = bq27x00_simple_value(di->cache.energy, val);
break;
+ case POWER_SUPPLY_PROP_POWER_AVG:
+ ret = bq27x00_simple_value(di->cache.power_avg, val);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = bq27x00_simple_value(di->cache.health, val);
+ break;
default:
return -EINVAL;
}
@@ -570,8 +688,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
int ret;
di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
- di->bat.properties = bq27x00_battery_props;
- di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
+ di->chip = BQ27425;
+ if (di->chip == BQ27425) {
+ di->bat.properties = bq27425_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
+ } else {
+ di->bat.properties = bq27x00_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
+ }
di->bat.get_property = bq27x00_battery_get_property;
di->bat.external_power_changed = bq27x00_external_power_changed;
@@ -729,6 +853,7 @@ static int bq27x00_battery_remove(struct i2c_client *client)
static const struct i2c_device_id bq27x00_id[] = {
{ "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
{ "bq27500", BQ27500 },
+ { "bq27425", BQ27425 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27x00_id);
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 86935ec18954..526e5c931294 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -271,16 +271,13 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
if (enable) {
if (cm->emergency_stop)
return -EAGAIN;
- err = regulator_bulk_enable(desc->num_charger_regulators,
- desc->charger_regulators);
+ for (i = 0 ; i < desc->num_charger_regulators ; i++)
+ regulator_enable(desc->charger_regulators[i].consumer);
} else {
/*
* Abnormal battery state - Stop charging forcibly,
* even if charger was enabled at the other places
*/
- err = regulator_bulk_disable(desc->num_charger_regulators,
- desc->charger_regulators);
-
for (i = 0; i < desc->num_charger_regulators; i++) {
if (regulator_is_enabled(
desc->charger_regulators[i].consumer)) {
@@ -288,7 +285,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
desc->charger_regulators[i].consumer);
dev_warn(cm->dev,
"Disable regulator(%s) forcibly.\n",
- desc->charger_regulators[i].supply);
+ desc->charger_regulators[i].regulator_name);
}
}
}
@@ -994,11 +991,92 @@ int setup_charger_manager(struct charger_global_desc *gd)
}
EXPORT_SYMBOL_GPL(setup_charger_manager);
+/**
+ * charger_extcon_work - enable/diable charger according to the state
+ * of charger cable
+ *
+ * @work: work_struct of the function charger_extcon_work.
+ */
+static void charger_extcon_work(struct work_struct *work)
+{
+ struct charger_cable *cable =
+ container_of(work, struct charger_cable, wq);
+ int ret;
+
+ if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
+ ret = regulator_set_current_limit(cable->charger->consumer,
+ cable->min_uA, cable->max_uA);
+ if (ret < 0) {
+ pr_err("Cannot set current limit of %s (%s)\n",
+ cable->charger->regulator_name, cable->name);
+ return;
+ }
+
+ pr_info("Set current limit of %s : %duA ~ %duA\n",
+ cable->charger->regulator_name,
+ cable->min_uA, cable->max_uA);
+ }
+
+ try_charger_enable(cable->cm, cable->attached);
+}
+
+/**
+ * charger_extcon_notifier - receive the state of charger cable
+ * when registered cable is attached or detached.
+ *
+ * @self: the notifier block of the charger_extcon_notifier.
+ * @event: the cable state.
+ * @ptr: the data pointer of notifier block.
+ */
+static int charger_extcon_notifier(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct charger_cable *cable =
+ container_of(self, struct charger_cable, nb);
+
+ cable->attached = event;
+ schedule_work(&cable->wq);
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * charger_extcon_init - register external connector to use it
+ * as the charger cable
+ *
+ * @cm: the Charger Manager representing the battery.
+ * @cable: the Charger cable representing the external connector.
+ */
+static int charger_extcon_init(struct charger_manager *cm,
+ struct charger_cable *cable)
+{
+ int ret = 0;
+
+ /*
+ * Charger manager use Extcon framework to identify
+ * the charger cable among various external connector
+ * cable (e.g., TA, USB, MHL, Dock).
+ */
+ INIT_WORK(&cable->wq, charger_extcon_work);
+ cable->nb.notifier_call = charger_extcon_notifier;
+ ret = extcon_register_interest(&cable->extcon_dev,
+ cable->extcon_name, cable->name, &cable->nb);
+ if (ret < 0) {
+ pr_info("Cannot register extcon_dev for %s(cable: %s).\n",
+ cable->extcon_name,
+ cable->name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static int charger_manager_probe(struct platform_device *pdev)
{
struct charger_desc *desc = dev_get_platdata(&pdev->dev);
struct charger_manager *cm;
int ret = 0, i = 0;
+ int j = 0;
union power_supply_propval val;
if (g_desc && !rtc_dev && g_desc->rtc_name) {
@@ -1167,11 +1245,31 @@ static int charger_manager_probe(struct platform_device *pdev)
goto err_register;
}
- ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
- desc->charger_regulators);
- if (ret) {
- dev_err(&pdev->dev, "Cannot get charger regulators.\n");
- goto err_bulk_get;
+ for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+ struct charger_regulator *charger
+ = &desc->charger_regulators[i];
+
+ charger->consumer = regulator_get(&pdev->dev,
+ charger->regulator_name);
+ if (charger->consumer == NULL) {
+ dev_err(&pdev->dev, "Cannot find charger(%s)n",
+ charger->regulator_name);
+ ret = -EINVAL;
+ goto err_chg_get;
+ }
+
+ for (j = 0 ; j < charger->num_cables ; j++) {
+ struct charger_cable *cable = &charger->cables[j];
+
+ ret = charger_extcon_init(cm, cable);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot find charger(%s)n",
+ charger->regulator_name);
+ goto err_extcon;
+ }
+ cable->charger = charger;
+ cable->cm = cm;
+ }
}
ret = try_charger_enable(cm, true);
@@ -1197,9 +1295,19 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0;
err_chg_enable:
- regulator_bulk_free(desc->num_charger_regulators,
- desc->charger_regulators);
-err_bulk_get:
+err_extcon:
+ for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+ struct charger_regulator *charger
+ = &desc->charger_regulators[i];
+ for (j = 0 ; j < charger->num_cables ; j++) {
+ struct charger_cable *cable = &charger->cables[j];
+ extcon_unregister_interest(&cable->extcon_dev);
+ }
+ }
+err_chg_get:
+ for (i = 0 ; i < desc->num_charger_regulators ; i++)
+ regulator_put(desc->charger_regulators[i].consumer);
+
power_supply_unregister(&cm->charger_psy);
err_register:
kfree(cm->charger_psy.properties);
@@ -1218,6 +1326,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
{
struct charger_manager *cm = platform_get_drvdata(pdev);
struct charger_desc *desc = cm->desc;
+ int i = 0;
+ int j = 0;
/* Remove from the list */
mutex_lock(&cm_list_mtx);
@@ -1229,8 +1339,18 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
if (delayed_work_pending(&cm_monitor_work))
cancel_delayed_work_sync(&cm_monitor_work);
- regulator_bulk_free(desc->num_charger_regulators,
- desc->charger_regulators);
+ for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+ struct charger_regulator *charger
+ = &desc->charger_regulators[i];
+ for (j = 0 ; j < charger->num_cables ; j++) {
+ struct charger_cable *cable = &charger->cables[j];
+ extcon_unregister_interest(&cable->extcon_dev);
+ }
+ }
+
+ for (i = 0 ; i < desc->num_charger_regulators ; i++)
+ regulator_put(desc->charger_regulators[i].consumer);
+
power_supply_unregister(&cm->charger_psy);
try_charger_enable(cm, false);
diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c
index 5f92a4bb33f9..7a1ff4e4cf9a 100644
--- a/drivers/power/ds2781_battery.c
+++ b/drivers/power/ds2781_battery.c
@@ -64,7 +64,7 @@ static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
}
-int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
+static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
int addr, size_t count)
{
return ds2781_battery_io(dev_info, buf, addr, count, 0);
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c
index 8672c9177dd7..cb2aa3195687 100644
--- a/drivers/power/gpio-charger.c
+++ b/drivers/power/gpio-charger.c
@@ -54,7 +54,7 @@ static int gpio_charger_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
- val->intval = gpio_get_value(pdata->gpio);
+ val->intval = gpio_get_value_cansleep(pdata->gpio);
val->intval ^= pdata->gpio_active_low;
break;
default:
diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c
index d8b75780bfef..6a364f4798f7 100644
--- a/drivers/power/lp8727_charger.c
+++ b/drivers/power/lp8727_charger.c
@@ -15,7 +15,7 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/power_supply.h>
-#include <linux/lp8727.h>
+#include <linux/platform_data/lp8727.h>
#define DEBOUNCE_MSEC 270
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 140788b309f8..74abc6c755b4 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -113,6 +113,7 @@ static enum power_supply_property max17042_battery_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
@@ -201,6 +202,13 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = ret * 1000 / 2;
break;
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ ret = max17042_read_reg(chip->client, MAX17042_QH);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret * 1000 / 2;
+ break;
case POWER_SUPPLY_PROP_TEMP:
ret = max17042_read_reg(chip->client, MAX17042_TEMP);
if (ret < 0)
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
index 7385092f9bc8..55b10b813353 100644
--- a/drivers/power/olpc_battery.c
+++ b/drivers/power/olpc_battery.c
@@ -231,11 +231,9 @@ static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
case POWER_SUPPLY_TECHNOLOGY_LiFe:
switch (mfr) {
- case 1: /* Gold Peak */
- val->intval = 2800000;
- break;
+ case 1: /* Gold Peak, fall through */
case 2: /* BYD */
- val->intval = 3100000;
+ val->intval = 2800000;
break;
default:
return -EIO;
@@ -267,6 +265,55 @@ static int olpc_bat_get_charge_now(union power_supply_propval *val)
return 0;
}
+static int olpc_bat_get_voltage_max_design(union power_supply_propval *val)
+{
+ uint8_t ec_byte;
+ union power_supply_propval tech;
+ int mfr;
+ int ret;
+
+ ret = olpc_bat_get_tech(&tech);
+ if (ret)
+ return ret;
+
+ ec_byte = BAT_ADDR_MFR_TYPE;
+ ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+ if (ret)
+ return ret;
+
+ mfr = ec_byte >> 4;
+
+ switch (tech.intval) {
+ case POWER_SUPPLY_TECHNOLOGY_NiMH:
+ switch (mfr) {
+ case 1: /* Gold Peak */
+ val->intval = 6000000;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+
+ case POWER_SUPPLY_TECHNOLOGY_LiFe:
+ switch (mfr) {
+ case 1: /* Gold Peak */
+ val->intval = 6400000;
+ break;
+ case 2: /* BYD */
+ val->intval = 6500000;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+
+ default:
+ return -EIO;
+ }
+
+ return ret;
+}
+
/*********************************************************************
* Battery properties
*********************************************************************/
@@ -401,6 +448,11 @@ static int olpc_bat_get_property(struct power_supply *psy,
sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
val->strval = bat_serial;
break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ ret = olpc_bat_get_voltage_max_design(val);
+ if (ret)
+ return ret;
+ break;
default:
ret = -EINVAL;
break;
@@ -428,6 +480,7 @@ static enum power_supply_property olpc_xo1_bat_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
};
/* XO-1.5 does not have ambient temperature property */
@@ -449,6 +502,7 @@ static enum power_supply_property olpc_xo15_bat_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
};
/* EEPROM reading goes completely around the power_supply API, sadly */
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
index 8dbcd53c5e67..7312f2651647 100644
--- a/drivers/power/pda_power.c
+++ b/drivers/power/pda_power.c
@@ -24,11 +24,7 @@
static inline unsigned int get_irq_flags(struct resource *res)
{
- unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED;
-
- flags |= res->flags & IRQF_TRIGGER_MASK;
-
- return flags;
+ return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
}
static struct device *dev;
@@ -134,13 +130,13 @@ static void update_charger(void)
regulator_set_current_limit(ac_draw, max_uA, max_uA);
if (!regulator_enabled) {
dev_dbg(dev, "charger on (AC)\n");
- regulator_enable(ac_draw);
+ WARN_ON(regulator_enable(ac_draw));
regulator_enabled = 1;
}
} else {
if (regulator_enabled) {
dev_dbg(dev, "charger off\n");
- regulator_disable(ac_draw);
+ WARN_ON(regulator_disable(ac_draw));
regulator_enabled = 0;
}
}
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 6ad612726785..08cc8a3c15af 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/power_supply.h>
+#include <linux/thermal.h>
#include "power_supply.h"
/* exported for the APM Power driver, APM emulation */
@@ -169,6 +170,63 @@ static void power_supply_dev_release(struct device *dev)
kfree(dev);
}
+#ifdef CONFIG_THERMAL
+static int power_supply_read_temp(struct thermal_zone_device *tzd,
+ unsigned long *temp)
+{
+ struct power_supply *psy;
+ union power_supply_propval val;
+ int ret;
+
+ WARN_ON(tzd == NULL);
+ psy = tzd->devdata;
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
+
+ /* Convert tenths of degree Celsius to milli degree Celsius. */
+ if (!ret)
+ *temp = val.intval * 100;
+
+ return ret;
+}
+
+static struct thermal_zone_device_ops psy_tzd_ops = {
+ .get_temp = power_supply_read_temp,
+};
+
+static int psy_register_thermal(struct power_supply *psy)
+{
+ int i;
+
+ /* Register battery zone device psy reports temperature */
+ for (i = 0; i < psy->num_properties; i++) {
+ if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
+ psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
+ psy, &psy_tzd_ops, 0, 0, 0, 0);
+ if (IS_ERR(psy->tzd))
+ return PTR_ERR(psy->tzd);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+ if (IS_ERR_OR_NULL(psy->tzd))
+ return;
+ thermal_zone_device_unregister(psy->tzd);
+}
+#else
+static int psy_register_thermal(struct power_supply *psy)
+{
+ return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+}
+#endif
+
int power_supply_register(struct device *parent, struct power_supply *psy)
{
struct device *dev;
@@ -197,6 +255,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
if (rc)
goto device_add_failed;
+ rc = psy_register_thermal(psy);
+ if (rc)
+ goto register_thermal_failed;
+
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
@@ -206,6 +268,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
goto success;
create_triggers_failed:
+ psy_unregister_thermal(psy);
+register_thermal_failed:
device_del(dev);
kobject_set_name_failed:
device_add_failed:
@@ -220,6 +284,7 @@ void power_supply_unregister(struct power_supply *psy)
cancel_work_sync(&psy->changed_work);
sysfs_remove_link(&psy->dev->kobj, "powers");
power_supply_remove_triggers(psy);
+ psy_unregister_thermal(psy);
device_unregister(psy->dev);
}
EXPORT_SYMBOL_GPL(power_supply_unregister);
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 4150747f9186..1d96614a17a4 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -159,6 +159,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(charge_now),
POWER_SUPPLY_ATTR(charge_avg),
POWER_SUPPLY_ATTR(charge_counter),
+ POWER_SUPPLY_ATTR(constant_charge_current),
+ POWER_SUPPLY_ATTR(constant_charge_voltage),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full),
@@ -166,9 +168,15 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(energy_now),
POWER_SUPPLY_ATTR(energy_avg),
POWER_SUPPLY_ATTR(capacity),
+ POWER_SUPPLY_ATTR(capacity_alert_min),
+ POWER_SUPPLY_ATTR(capacity_alert_max),
POWER_SUPPLY_ATTR(capacity_level),
POWER_SUPPLY_ATTR(temp),
+ POWER_SUPPLY_ATTR(temp_alert_min),
+ POWER_SUPPLY_ATTR(temp_alert_max),
POWER_SUPPLY_ATTR(temp_ambient),
+ POWER_SUPPLY_ATTR(temp_ambient_alert_min),
+ POWER_SUPPLY_ATTR(temp_ambient_alert_max),
POWER_SUPPLY_ATTR(time_to_empty_now),
POWER_SUPPLY_ATTR(time_to_empty_avg),
POWER_SUPPLY_ATTR(time_to_full_now),
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c
index a5b6849d4123..a65e8f54157e 100644
--- a/drivers/power/sbs-battery.c
+++ b/drivers/power/sbs-battery.c
@@ -469,7 +469,7 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
+ goto done; /* don't trigger power_supply_changed()! */
case POWER_SUPPLY_PROP_ENERGY_NOW:
case POWER_SUPPLY_PROP_ENERGY_FULL:
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c
index f8eedd8a676f..332dd0110bda 100644
--- a/drivers/power/smb347-charger.c
+++ b/drivers/power/smb347-charger.c
@@ -196,6 +196,14 @@ static const unsigned int ccc_tbl[] = {
1200000,
};
+/* Convert register value to current using lookup table */
+static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
+{
+ if (val >= size)
+ return -EINVAL;
+ return tbl[val];
+}
+
/* Convert current to register value using lookup table */
static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
{
@@ -841,22 +849,101 @@ fail:
return ret;
}
+/*
+ * Returns the constant charge current programmed
+ * into the charger in uA.
+ */
+static int get_const_charge_current(struct smb347_charger *smb)
+{
+ int ret, intval;
+ unsigned int v;
+
+ if (!smb347_is_ps_online(smb))
+ return -ENODATA;
+
+ ret = regmap_read(smb->regmap, STAT_B, &v);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The current value is composition of FCC and PCC values
+ * and we can detect which table to use from bit 5.
+ */
+ if (v & 0x20) {
+ intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7);
+ } else {
+ v >>= 3;
+ intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7);
+ }
+
+ return intval;
+}
+
+/*
+ * Returns the constant charge voltage programmed
+ * into the charger in uV.
+ */
+static int get_const_charge_voltage(struct smb347_charger *smb)
+{
+ int ret, intval;
+ unsigned int v;
+
+ if (!smb347_is_ps_online(smb))
+ return -ENODATA;
+
+ ret = regmap_read(smb->regmap, STAT_A, &v);
+ if (ret < 0)
+ return ret;
+
+ v &= STAT_A_FLOAT_VOLTAGE_MASK;
+ if (v > 0x3d)
+ v = 0x3d;
+
+ intval = 3500000 + v * 20000;
+
+ return intval;
+}
+
static int smb347_mains_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct smb347_charger *smb =
container_of(psy, struct smb347_charger, mains);
+ int ret;
- if (prop == POWER_SUPPLY_PROP_ONLINE) {
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
val->intval = smb->mains_online;
- return 0;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = get_const_charge_voltage(smb);
+ if (ret < 0)
+ return ret;
+ else
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = get_const_charge_current(smb);
+ if (ret < 0)
+ return ret;
+ else
+ val->intval = ret;
+ break;
+
+ default:
+ return -EINVAL;
}
- return -EINVAL;
+
+ return 0;
}
static enum power_supply_property smb347_mains_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
};
static int smb347_usb_get_property(struct power_supply *psy,
@@ -865,16 +952,40 @@ static int smb347_usb_get_property(struct power_supply *psy,
{
struct smb347_charger *smb =
container_of(psy, struct smb347_charger, usb);
+ int ret;
- if (prop == POWER_SUPPLY_PROP_ONLINE) {
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
val->intval = smb->usb_online;
- return 0;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = get_const_charge_voltage(smb);
+ if (ret < 0)
+ return ret;
+ else
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = get_const_charge_current(smb);
+ if (ret < 0)
+ return ret;
+ else
+ val->intval = ret;
+ break;
+
+ default:
+ return -EINVAL;
}
- return -EINVAL;
+
+ return 0;
}
static enum power_supply_property smb347_usb_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
};
static int smb347_battery_get_property(struct power_supply *psy,
diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c
index b527c93bf2f3..b99a452a4fda 100644
--- a/drivers/power/test_power.c
+++ b/drivers/power/test_power.c
@@ -22,11 +22,13 @@
#include <linux/vermagic.h>
static int ac_online = 1;
+static int usb_online = 1;
static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
static int battery_health = POWER_SUPPLY_HEALTH_GOOD;
static int battery_present = 1; /* true */
static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION;
static int battery_capacity = 50;
+static int battery_voltage = 3300;
static int test_power_get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
@@ -42,6 +44,20 @@ static int test_power_get_ac_property(struct power_supply *psy,
return 0;
}
+static int test_power_get_usb_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = usb_online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int test_power_get_battery_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -86,6 +102,12 @@ static int test_power_get_battery_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
val->intval = 3600;
break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = 26;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = battery_voltage;
+ break;
default:
pr_info("%s: some properties deliberately report errors.\n",
__func__);
@@ -114,6 +136,8 @@ static enum power_supply_property test_power_battery_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
static char *test_power_ac_supplied_to[] = {
@@ -135,6 +159,14 @@ static struct power_supply test_power_supplies[] = {
.properties = test_power_battery_props,
.num_properties = ARRAY_SIZE(test_power_battery_props),
.get_property = test_power_get_battery_property,
+ }, {
+ .name = "test_usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .supplied_to = test_power_ac_supplied_to,
+ .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
+ .properties = test_power_ac_props,
+ .num_properties = ARRAY_SIZE(test_power_ac_props),
+ .get_property = test_power_get_usb_property,
},
};
@@ -167,6 +199,7 @@ static void __exit test_power_exit(void)
/* Let's see how we handle changes... */
ac_online = 0;
+ usb_online = 0;
battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
power_supply_changed(&test_power_supplies[i]);
@@ -275,6 +308,19 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
return strlen(buffer);
}
+static int param_set_usb_online(const char *key, const struct kernel_param *kp)
+{
+ usb_online = map_get_value(map_ac_online, key, usb_online);
+ power_supply_changed(&test_power_supplies[2]);
+ return 0;
+}
+
+static int param_get_usb_online(char *buffer, const struct kernel_param *kp)
+{
+ strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown"));
+ return strlen(buffer);
+}
+
static int param_set_battery_status(const char *key,
const struct kernel_param *kp)
{
@@ -350,13 +396,31 @@ static int param_set_battery_capacity(const char *key,
#define param_get_battery_capacity param_get_int
+static int param_set_battery_voltage(const char *key,
+ const struct kernel_param *kp)
+{
+ int tmp;
+
+ if (1 != sscanf(key, "%d", &tmp))
+ return -EINVAL;
+
+ battery_voltage = tmp;
+ power_supply_changed(&test_power_supplies[1]);
+ return 0;
+}
+#define param_get_battery_voltage param_get_int
static struct kernel_param_ops param_ops_ac_online = {
.set = param_set_ac_online,
.get = param_get_ac_online,
};
+static struct kernel_param_ops param_ops_usb_online = {
+ .set = param_set_usb_online,
+ .get = param_get_usb_online,
+};
+
static struct kernel_param_ops param_ops_battery_status = {
.set = param_set_battery_status,
.get = param_get_battery_status,
@@ -382,18 +446,27 @@ static struct kernel_param_ops param_ops_battery_capacity = {
.get = param_get_battery_capacity,
};
+static struct kernel_param_ops param_ops_battery_voltage = {
+ .set = param_set_battery_voltage,
+ .get = param_get_battery_voltage,
+};
#define param_check_ac_online(name, p) __param_check(name, p, void);
+#define param_check_usb_online(name, p) __param_check(name, p, void);
#define param_check_battery_status(name, p) __param_check(name, p, void);
#define param_check_battery_present(name, p) __param_check(name, p, void);
#define param_check_battery_technology(name, p) __param_check(name, p, void);
#define param_check_battery_health(name, p) __param_check(name, p, void);
#define param_check_battery_capacity(name, p) __param_check(name, p, void);
+#define param_check_battery_voltage(name, p) __param_check(name, p, void);
module_param(ac_online, ac_online, 0644);
MODULE_PARM_DESC(ac_online, "AC charging state <on|off>");
+module_param(usb_online, usb_online, 0644);
+MODULE_PARM_DESC(usb_online, "USB charging state <on|off>");
+
module_param(battery_status, battery_status, 0644);
MODULE_PARM_DESC(battery_status,
"battery status <charging|discharging|not-charging|full>");
@@ -413,6 +486,8 @@ MODULE_PARM_DESC(battery_health,
module_param(battery_capacity, battery_capacity, 0644);
MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
+module_param(battery_voltage, battery_voltage, 0644);
+MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)");
MODULE_DESCRIPTION("Power supply driver for testing");
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index 7cacbaa68efe..15f4d5d8611b 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -22,6 +22,7 @@
#include <linux/power_supply.h>
#include <linux/notifier.h>
#include <linux/usb/otg.h>
+#include <linux/regulator/machine.h>
#define TWL4030_BCIMSTATEC 0x02
#define TWL4030_BCIICHG 0x08
@@ -29,6 +30,7 @@
#define TWL4030_BCIVBUS 0x0c
#define TWL4030_BCIMFSTS4 0x10
#define TWL4030_BCICTL1 0x23
+#define TWL4030_BB_CFG 0x12
#define TWL4030_BCIAUTOWEN BIT(5)
#define TWL4030_CONFIG_DONE BIT(4)
@@ -38,6 +40,17 @@
#define TWL4030_USBFASTMCHG BIT(2)
#define TWL4030_STS_VBUS BIT(7)
#define TWL4030_STS_USB_ID BIT(2)
+#define TWL4030_BBCHEN BIT(4)
+#define TWL4030_BBSEL_MASK 0b1100
+#define TWL4030_BBSEL_2V5 0b0000
+#define TWL4030_BBSEL_3V0 0b0100
+#define TWL4030_BBSEL_3V1 0b1000
+#define TWL4030_BBSEL_3V2 0b1100
+#define TWL4030_BBISEL_MASK 0b11
+#define TWL4030_BBISEL_25uA 0b00
+#define TWL4030_BBISEL_150uA 0b01
+#define TWL4030_BBISEL_500uA 0b10
+#define TWL4030_BBISEL_1000uA 0b11
/* BCI interrupts */
#define TWL4030_WOVF BIT(0) /* Watchdog overflow */
@@ -75,6 +88,8 @@ struct twl4030_bci {
struct work_struct work;
int irq_chg;
int irq_bci;
+ struct regulator *usb_reg;
+ int usb_enabled;
unsigned long event;
};
@@ -104,7 +119,7 @@ static int twl4030_bci_read(u8 reg, u8 *val)
static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
{
- return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
+ return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear,
TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
TWL4030_PM_MASTER_BOOT_BCI);
}
@@ -152,14 +167,14 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
}
/*
- * Enable/Disable USB Charge funtionality.
+ * Enable/Disable USB Charge functionality.
*/
static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
{
int ret;
if (enable) {
- /* Check for USB charger conneted */
+ /* Check for USB charger connected */
if (!twl4030_bci_have_vbus(bci))
return -ENODEV;
@@ -172,6 +187,12 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
return -EACCES;
}
+ /* Need to keep regulator on */
+ if (!bci->usb_enabled) {
+ regulator_enable(bci->usb_reg);
+ bci->usb_enabled = 1;
+ }
+
/* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
if (ret < 0)
@@ -182,6 +203,10 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
} else {
ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+ if (bci->usb_enabled) {
+ regulator_disable(bci->usb_reg);
+ bci->usb_enabled = 0;
+ }
}
return ret;
@@ -203,6 +228,49 @@ static int twl4030_charger_enable_ac(bool enable)
}
/*
+ * Enable/Disable charging of Backup Battery.
+ */
+static int twl4030_charger_enable_backup(int uvolt, int uamp)
+{
+ int ret;
+ u8 flags;
+
+ if (uvolt < 2500000 ||
+ uamp < 25) {
+ /* disable charging of backup battery */
+ ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+ TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
+ return ret;
+ }
+
+ flags = TWL4030_BBCHEN;
+ if (uvolt >= 3200000)
+ flags |= TWL4030_BBSEL_3V2;
+ else if (uvolt >= 3100000)
+ flags |= TWL4030_BBSEL_3V1;
+ else if (uvolt >= 3000000)
+ flags |= TWL4030_BBSEL_3V0;
+ else
+ flags |= TWL4030_BBSEL_2V5;
+
+ if (uamp >= 1000)
+ flags |= TWL4030_BBISEL_1000uA;
+ else if (uamp >= 500)
+ flags |= TWL4030_BBISEL_500uA;
+ else if (uamp >= 150)
+ flags |= TWL4030_BBISEL_150uA;
+ else
+ flags |= TWL4030_BBISEL_25uA;
+
+ ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+ TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
+ flags,
+ TWL4030_BB_CFG);
+
+ return ret;
+}
+
+/*
* TWL4030 CHG_PRES (AC charger presence) events
*/
static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
@@ -425,6 +493,7 @@ static enum power_supply_property twl4030_charger_props[] = {
static int __init twl4030_bci_probe(struct platform_device *pdev)
{
struct twl4030_bci *bci;
+ struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
int ret;
u32 reg;
@@ -456,6 +525,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
bci->usb.get_property = twl4030_bci_get_property;
+ bci->usb_reg = regulator_get(bci->dev, "bci3v1");
+
ret = power_supply_register(&pdev->dev, &bci->usb);
if (ret) {
dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
@@ -504,6 +575,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
twl4030_charger_enable_ac(true);
twl4030_charger_enable_usb(bci, true);
+ twl4030_charger_enable_backup(pdata->bb_uvolt,
+ pdata->bb_uamp);
return 0;
@@ -532,6 +605,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
twl4030_charger_enable_ac(false);
twl4030_charger_enable_usb(bci, false);
+ twl4030_charger_enable_backup(0, 0);
/* mask interrupts */
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 98fbe62694d4..e771487132f7 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -327,8 +327,10 @@ int pps_register_cdev(struct pps_device *pps)
}
pps->dev = device_create(pps_class, pps->info.dev, devt, pps,
"pps%d", pps->id);
- if (IS_ERR(pps->dev))
+ if (IS_ERR(pps->dev)) {
+ err = PTR_ERR(pps->dev);
goto del_cdev;
+ }
pps->dev->release = pps_device_destruct;
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 000000000000..8fc3808d7a3e
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,108 @@
+menuconfig PWM
+ bool "PWM Support"
+ depends on !MACH_JZ4740 && !PUV3_PWM
+ help
+ This enables PWM support through the generic PWM framework.
+ You only need to enable this, if you also want to enable
+ one or more of the PWM drivers below.
+
+ If unsure, say N.
+
+if PWM
+
+config PWM_BFIN
+ tristate "Blackfin PWM support"
+ depends on BFIN_GPTIMERS
+ help
+ Generic PWM framework driver for Blackfin.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-bfin.
+
+config PWM_IMX
+ tristate "i.MX pwm support"
+ depends on ARCH_MXC
+ help
+ Generic PWM framework driver for i.MX.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-imx.
+
+config PWM_LPC32XX
+ tristate "LPC32XX PWM support"
+ depends on ARCH_LPC32XX
+ help
+ Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two
+ PWM controllers.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpc32xx.
+
+config PWM_MXS
+ tristate "Freescale MXS PWM support"
+ depends on ARCH_MXS && OF
+ select STMP_DEVICE
+ help
+ Generic PWM framework driver for Freescale MXS.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-mxs.
+
+config PWM_PXA
+ tristate "PXA PWM support"
+ depends on ARCH_PXA
+ help
+ Generic PWM framework driver for PXA.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-pxa.
+
+config PWM_SAMSUNG
+ tristate "Samsung pwm support"
+ depends on PLAT_SAMSUNG
+ help
+ Generic PWM framework driver for Samsung.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-samsung.
+
+config PWM_TEGRA
+ tristate "NVIDIA Tegra PWM support"
+ depends on ARCH_TEGRA
+ help
+ Generic PWM framework driver for the PWFM controller found on NVIDIA
+ Tegra SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tegra.
+
+config PWM_TIECAP
+ tristate "ECAP PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the ECAP APWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tiecap.
+
+config PWM_TIEHRPWM
+ tristate "EHRPWM PWM support"
+ depends on SOC_AM33XX
+ help
+ PWM driver support for the EHRPWM controller found on AM33XX
+ TI SOC
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tiehrpwm.
+
+config PWM_VT8500
+ tristate "vt8500 pwm support"
+ depends on ARCH_VT8500
+ help
+ Generic PWM framework driver for vt8500.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-vt8500.
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 000000000000..e4b2c898964d
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_PWM) += core.o
+obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
+obj-$(CONFIG_PWM_IMX) += pwm-imx.o
+obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o
+obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
+obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
+obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
+obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
+obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o
+obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o
+obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644
index 000000000000..ecb76909e946
--- /dev/null
+++ b/drivers/pwm/core.c
@@ -0,0 +1,713 @@
+/*
+ * Generic pwmlib implementation
+ *
+ * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/radix-tree.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define MAX_PWMS 1024
+
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_chips);
+static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
+static RADIX_TREE(pwm_tree, GFP_KERNEL);
+
+static struct pwm_device *pwm_to_device(unsigned int pwm)
+{
+ return radix_tree_lookup(&pwm_tree, pwm);
+}
+
+static int alloc_pwms(int pwm, unsigned int count)
+{
+ unsigned int from = 0;
+ unsigned int start;
+
+ if (pwm >= MAX_PWMS)
+ return -EINVAL;
+
+ if (pwm >= 0)
+ from = pwm;
+
+ start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
+ count, 0);
+
+ if (pwm >= 0 && start != pwm)
+ return -EEXIST;
+
+ if (start + count > MAX_PWMS)
+ return -ENOSPC;
+
+ return start;
+}
+
+static void free_pwms(struct pwm_chip *chip)
+{
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ radix_tree_delete(&pwm_tree, pwm->pwm);
+ }
+
+ bitmap_clear(allocated_pwms, chip->base, chip->npwm);
+
+ kfree(chip->pwms);
+ chip->pwms = NULL;
+}
+
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+ struct pwm_chip *chip;
+
+ if (!name)
+ return NULL;
+
+ mutex_lock(&pwm_lock);
+
+ list_for_each_entry(chip, &pwm_chips, list) {
+ const char *chip_name = dev_name(chip->dev);
+
+ if (chip_name && strcmp(chip_name, name) == 0) {
+ mutex_unlock(&pwm_lock);
+ return chip;
+ }
+ }
+
+ mutex_unlock(&pwm_lock);
+
+ return NULL;
+}
+
+static int pwm_device_request(struct pwm_device *pwm, const char *label)
+{
+ int err;
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags))
+ return -EBUSY;
+
+ if (!try_module_get(pwm->chip->ops->owner))
+ return -ENODEV;
+
+ if (pwm->chip->ops->request) {
+ err = pwm->chip->ops->request(pwm->chip, pwm);
+ if (err) {
+ module_put(pwm->chip->ops->owner);
+ return err;
+ }
+ }
+
+ set_bit(PWMF_REQUESTED, &pwm->flags);
+ pwm->label = label;
+
+ return 0;
+}
+
+static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
+ const struct of_phandle_args *args)
+{
+ struct pwm_device *pwm;
+
+ if (pc->of_pwm_n_cells < 2)
+ return ERR_PTR(-EINVAL);
+
+ if (args->args[0] >= pc->npwm)
+ return ERR_PTR(-EINVAL);
+
+ pwm = pwm_request_from_chip(pc, args->args[0], NULL);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ pwm_set_period(pwm, args->args[1]);
+
+ return pwm;
+}
+
+void of_pwmchip_add(struct pwm_chip *chip)
+{
+ if (!chip->dev || !chip->dev->of_node)
+ return;
+
+ if (!chip->of_xlate) {
+ chip->of_xlate = of_pwm_simple_xlate;
+ chip->of_pwm_n_cells = 2;
+ }
+
+ of_node_get(chip->dev->of_node);
+}
+
+void of_pwmchip_remove(struct pwm_chip *chip)
+{
+ if (chip->dev && chip->dev->of_node)
+ of_node_put(chip->dev->of_node);
+}
+
+/**
+ * pwm_set_chip_data() - set private chip data for a PWM
+ * @pwm: PWM device
+ * @data: pointer to chip-specific data
+ */
+int pwm_set_chip_data(struct pwm_device *pwm, void *data)
+{
+ if (!pwm)
+ return -EINVAL;
+
+ pwm->chip_data = data;
+
+ return 0;
+}
+
+/**
+ * pwm_get_chip_data() - get private chip data for a PWM
+ * @pwm: PWM device
+ */
+void *pwm_get_chip_data(struct pwm_device *pwm)
+{
+ return pwm ? pwm->chip_data : NULL;
+}
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used.
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+ struct pwm_device *pwm;
+ unsigned int i;
+ int ret;
+
+ if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
+ !chip->ops->enable || !chip->ops->disable)
+ return -EINVAL;
+
+ mutex_lock(&pwm_lock);
+
+ ret = alloc_pwms(chip->base, chip->npwm);
+ if (ret < 0)
+ goto out;
+
+ chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
+ if (!chip->pwms) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ chip->base = ret;
+
+ for (i = 0; i < chip->npwm; i++) {
+ pwm = &chip->pwms[i];
+
+ pwm->chip = chip;
+ pwm->pwm = chip->base + i;
+ pwm->hwpwm = i;
+
+ radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
+ }
+
+ bitmap_set(allocated_pwms, chip->base, chip->npwm);
+
+ INIT_LIST_HEAD(&chip->list);
+ list_add(&chip->list, &pwm_chips);
+
+ ret = 0;
+
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_add(chip);
+
+out:
+ mutex_unlock(&pwm_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_add);
+
+/**
+ * pwmchip_remove() - remove a PWM chip
+ * @chip: the PWM chip to remove
+ *
+ * Removes a PWM chip. This function may return busy if the PWM chip provides
+ * a PWM device that is still requested.
+ */
+int pwmchip_remove(struct pwm_chip *chip)
+{
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&pwm_lock);
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ list_del_init(&chip->list);
+
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_remove(chip);
+
+ free_pwms(chip);
+
+out:
+ mutex_unlock(&pwm_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pwmchip_remove);
+
+/**
+ * pwm_request() - request a PWM device
+ * @pwm_id: global PWM device index
+ * @label: PWM device label
+ *
+ * This function is deprecated, use pwm_get() instead.
+ */
+struct pwm_device *pwm_request(int pwm, const char *label)
+{
+ struct pwm_device *dev;
+ int err;
+
+ if (pwm < 0 || pwm >= MAX_PWMS)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&pwm_lock);
+
+ dev = pwm_to_device(pwm);
+ if (!dev) {
+ dev = ERR_PTR(-EPROBE_DEFER);
+ goto out;
+ }
+
+ err = pwm_device_request(dev, label);
+ if (err < 0)
+ dev = ERR_PTR(err);
+
+out:
+ mutex_unlock(&pwm_lock);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_request_from_chip() - request a PWM device relative to a PWM chip
+ * @chip: PWM chip
+ * @index: per-chip index of the PWM to request
+ * @label: a literal description string of this PWM
+ *
+ * Returns the PWM at the given index of the given PWM chip. A negative error
+ * code is returned if the index is not valid for the specified PWM chip or
+ * if the PWM device cannot be requested.
+ */
+struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+ unsigned int index,
+ const char *label)
+{
+ struct pwm_device *pwm;
+ int err;
+
+ if (!chip || index >= chip->npwm)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&pwm_lock);
+ pwm = &chip->pwms[index];
+
+ err = pwm_device_request(pwm, label);
+ if (err < 0)
+ pwm = ERR_PTR(err);
+
+ mutex_unlock(&pwm_lock);
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request_from_chip);
+
+/**
+ * pwm_free() - free a PWM device
+ * @pwm: PWM device
+ *
+ * This function is deprecated, use pwm_put() instead.
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+ pwm_put(pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config() - change a PWM device configuration
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ */
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ if (!pwm || period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable() - start a PWM output toggling
+ * @pwm: PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+ if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
+ return pwm->chip->ops->enable(pwm->chip, pwm);
+
+ return pwm ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable() - stop a PWM output toggling
+ * @pwm: PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+ if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
+ pwm->chip->ops->disable(pwm->chip, pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
+{
+ struct pwm_chip *chip;
+
+ mutex_lock(&pwm_lock);
+
+ list_for_each_entry(chip, &pwm_chips, list)
+ if (chip->dev && chip->dev->of_node == np) {
+ mutex_unlock(&pwm_lock);
+ return chip;
+ }
+
+ mutex_unlock(&pwm_lock);
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * of_pwm_request() - request a PWM via the PWM framework
+ * @np: device node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the phandle and index specified in the
+ * "pwms" property of a device tree node or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * If con_id is NULL, the first PWM device listed in the "pwms" property will
+ * be requested. Otherwise the "pwm-names" property is used to do a reverse
+ * lookup of the PWM index. This also means that the "pwm-names" property
+ * becomes mandatory for devices that look up the PWM device via the con_id
+ * parameter.
+ */
+static struct pwm_device *of_pwm_request(struct device_node *np,
+ const char *con_id)
+{
+ struct pwm_device *pwm = NULL;
+ struct of_phandle_args args;
+ struct pwm_chip *pc;
+ int index = 0;
+ int err;
+
+ if (con_id) {
+ index = of_property_match_string(np, "pwm-names", con_id);
+ if (index < 0)
+ return ERR_PTR(index);
+ }
+
+ err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
+ &args);
+ if (err) {
+ pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+ return ERR_PTR(err);
+ }
+
+ pc = of_node_to_pwmchip(args.np);
+ if (IS_ERR(pc)) {
+ pr_debug("%s(): PWM chip not found\n", __func__);
+ pwm = ERR_CAST(pc);
+ goto put;
+ }
+
+ if (args.args_count != pc->of_pwm_n_cells) {
+ pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
+ args.np->full_name);
+ pwm = ERR_PTR(-EINVAL);
+ goto put;
+ }
+
+ pwm = pc->of_xlate(pc, &args);
+ if (IS_ERR(pwm))
+ goto put;
+
+ /*
+ * If a consumer name was not given, try to look it up from the
+ * "pwm-names" property if it exists. Otherwise use the name of
+ * the user device node.
+ */
+ if (!con_id) {
+ err = of_property_read_string_index(np, "pwm-names", index,
+ &con_id);
+ if (err < 0)
+ con_id = np->name;
+ }
+
+ pwm->label = con_id;
+
+put:
+ of_node_put(args.np);
+
+ return pwm;
+}
+
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void __init pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+ mutex_lock(&pwm_lookup_lock);
+
+ while (num--) {
+ list_add_tail(&table->list, &pwm_lookup_list);
+ table++;
+ }
+
+ mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Lookup is first attempted using DT. If the device was not instantiated from
+ * a device tree, a PWM chip and a relative index is looked up via a table
+ * supplied by board setup code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+ struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
+ const char *dev_id = dev ? dev_name(dev): NULL;
+ struct pwm_chip *chip = NULL;
+ unsigned int index = 0;
+ unsigned int best = 0;
+ struct pwm_lookup *p;
+ unsigned int match;
+
+ /* look up via DT first */
+ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
+ return of_pwm_request(dev->of_node, con_id);
+
+ /*
+ * We look up the provider in the static table typically provided by
+ * board setup code. We first try to lookup the consumer device by
+ * name. If the consumer device was passed in as NULL or if no match
+ * was found, we try to find the consumer by directly looking it up
+ * by name.
+ *
+ * If a match is found, the provider PWM chip is looked up by name
+ * and a PWM device is requested using the PWM device per-chip index.
+ *
+ * The lookup algorithm was shamelessly taken from the clock
+ * framework:
+ *
+ * We do slightly fuzzy matching here:
+ * An entry with a NULL ID is assumed to be a wildcard.
+ * If an entry has a device ID, it must match
+ * If an entry has a connection ID, it must match
+ * Then we take the most specific entry - with the following order
+ * of precedence: dev+con > dev only > con only.
+ */
+ mutex_lock(&pwm_lookup_lock);
+
+ list_for_each_entry(p, &pwm_lookup_list, list) {
+ match = 0;
+
+ if (p->dev_id) {
+ if (!dev_id || strcmp(p->dev_id, dev_id))
+ continue;
+
+ match += 2;
+ }
+
+ if (p->con_id) {
+ if (!con_id || strcmp(p->con_id, con_id))
+ continue;
+
+ match += 1;
+ }
+
+ if (match > best) {
+ chip = pwmchip_find_by_name(p->provider);
+ index = p->index;
+
+ if (match != 3)
+ best = match;
+ else
+ break;
+ }
+ }
+
+ if (chip)
+ pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
+
+ mutex_unlock(&pwm_lookup_lock);
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+ if (!pwm)
+ return;
+
+ mutex_lock(&pwm_lock);
+
+ if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+ pr_warning("PWM device already freed\n");
+ goto out;
+ }
+
+ if (pwm->chip->ops->free)
+ pwm->chip->ops->free(pwm->chip, pwm);
+
+ pwm->label = NULL;
+
+ module_put(pwm->chip->ops->owner);
+out:
+ mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
+#ifdef CONFIG_DEBUG_FS
+static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags))
+ seq_printf(s, " requested");
+
+ if (test_bit(PWMF_ENABLED, &pwm->flags))
+ seq_printf(s, " enabled");
+
+ seq_printf(s, "\n");
+ }
+}
+
+static void *pwm_seq_start(struct seq_file *s, loff_t *pos)
+{
+ mutex_lock(&pwm_lock);
+ s->private = "";
+
+ return seq_list_start(&pwm_chips, *pos);
+}
+
+static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ s->private = "\n";
+
+ return seq_list_next(v, &pwm_chips, pos);
+}
+
+static void pwm_seq_stop(struct seq_file *s, void *v)
+{
+ mutex_unlock(&pwm_lock);
+}
+
+static int pwm_seq_show(struct seq_file *s, void *v)
+{
+ struct pwm_chip *chip = list_entry(v, struct pwm_chip, list);
+
+ seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private,
+ chip->dev->bus ? chip->dev->bus->name : "no-bus",
+ dev_name(chip->dev), chip->npwm,
+ (chip->npwm != 1) ? "s" : "");
+
+ if (chip->ops->dbg_show)
+ chip->ops->dbg_show(chip, s);
+ else
+ pwm_dbg_show(chip, s);
+
+ return 0;
+}
+
+static const struct seq_operations pwm_seq_ops = {
+ .start = pwm_seq_start,
+ .next = pwm_seq_next,
+ .stop = pwm_seq_stop,
+ .show = pwm_seq_show,
+};
+
+static int pwm_seq_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &pwm_seq_ops);
+}
+
+static const struct file_operations pwm_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = pwm_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int __init pwm_debugfs_init(void)
+{
+ debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL,
+ &pwm_debugfs_ops);
+
+ return 0;
+}
+
+subsys_initcall(pwm_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
new file mode 100644
index 000000000000..d53c4e7941ef
--- /dev/null
+++ b/drivers/pwm/pwm-bfin.c
@@ -0,0 +1,162 @@
+/*
+ * Blackfin Pulse Width Modulation (PWM) core
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+struct bfin_pwm_chip {
+ struct pwm_chip chip;
+};
+
+struct bfin_pwm {
+ unsigned short pin;
+};
+
+static const unsigned short pwm_to_gptimer_per[] = {
+ P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
+ P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
+};
+
+static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct bfin_pwm *priv;
+ int ret;
+
+ if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
+
+ ret = peripheral_request(priv->pin, NULL);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+
+ pwm_set_chip_data(pwm, priv);
+
+ return 0;
+}
+
+static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+ if (priv) {
+ peripheral_free(priv->pin);
+ kfree(priv);
+ }
+}
+
+static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+ unsigned long period, duty;
+ unsigned long long val;
+
+ if (duty_ns < 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ val = (unsigned long long)get_sclk() * period_ns;
+ do_div(val, NSEC_PER_SEC);
+ period = val;
+
+ val = (unsigned long long)period * duty_ns;
+ do_div(val, period_ns);
+ duty = period - val;
+
+ if (duty >= period)
+ duty = period - 1;
+
+ set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
+ set_gptimer_pwidth(priv->pin, duty);
+ set_gptimer_period(priv->pin, period);
+
+ return 0;
+}
+
+static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+ enable_gptimer(priv->pin);
+
+ return 0;
+}
+
+static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct bfin_pwm *priv = pwm_get_chip_data(pwm);
+
+ disable_gptimer(priv->pin);
+}
+
+static struct pwm_ops bfin_pwm_ops = {
+ .request = bfin_pwm_request,
+ .free = bfin_pwm_free,
+ .config = bfin_pwm_config,
+ .enable = bfin_pwm_enable,
+ .disable = bfin_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int bfin_pwm_probe(struct platform_device *pdev)
+{
+ struct bfin_pwm_chip *pwm;
+ int ret;
+
+ pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+ if (!pwm) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, pwm);
+
+ pwm->chip.dev = &pdev->dev;
+ pwm->chip.ops = &bfin_pwm_ops;
+ pwm->chip.base = -1;
+ pwm->chip.npwm = 12;
+
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit bfin_pwm_remove(struct platform_device *pdev)
+{
+ struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&pwm->chip);
+}
+
+static struct platform_driver bfin_pwm_driver = {
+ .driver = {
+ .name = "bfin-pwm",
+ },
+ .probe = bfin_pwm_probe,
+ .remove = __devexit_p(bfin_pwm_remove),
+};
+
+module_platform_driver(bfin_pwm_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
new file mode 100644
index 000000000000..2a0b35333972
--- /dev/null
+++ b/drivers/pwm/pwm-imx.c
@@ -0,0 +1,230 @@
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * 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.
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <mach/hardware.h>
+
+
+/* i.MX1 and i.MX21 share the same PWM function block: */
+
+#define MX1_PWMC 0x00 /* PWM Control Register */
+#define MX1_PWMS 0x04 /* PWM Sample Register */
+#define MX1_PWMP 0x08 /* PWM Period Register */
+
+
+/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
+
+#define MX3_PWMCR 0x00 /* PWM Control Register */
+#define MX3_PWMSAR 0x0C /* PWM Sample Register */
+#define MX3_PWMPR 0x10 /* PWM Period Register */
+#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
+#define MX3_PWMCR_DOZEEN (1 << 24)
+#define MX3_PWMCR_WAITEN (1 << 23)
+#define MX3_PWMCR_DBGEN (1 << 22)
+#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
+#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
+#define MX3_PWMCR_EN (1 << 0)
+
+struct imx_chip {
+ struct clk *clk;
+
+ int clk_enabled;
+ void __iomem *mmio_base;
+
+ struct pwm_chip chip;
+};
+
+#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
+
+static int imx_pwm_config(struct pwm_chip *chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct imx_chip *imx = to_imx_chip(chip);
+
+ if (!(cpu_is_mx1() || cpu_is_mx21())) {
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles, prescale;
+ u32 cr;
+
+ c = clk_get_rate(imx->clk);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ prescale = period_cycles / 0x10000 + 1;
+
+ period_cycles /= prescale;
+ c = (unsigned long long)period_cycles * duty_ns;
+ do_div(c, period_ns);
+ duty_cycles = c;
+
+ /*
+ * according to imx pwm RM, the real period value should be
+ * PERIOD value in PWMPR plus 2.
+ */
+ if (period_cycles > 2)
+ period_cycles -= 2;
+ else
+ period_cycles = 0;
+
+ writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+ writel(period_cycles, imx->mmio_base + MX3_PWMPR);
+
+ cr = MX3_PWMCR_PRESCALER(prescale) |
+ MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
+ MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
+
+ if (cpu_is_mx25())
+ cr |= MX3_PWMCR_CLKSRC_IPG;
+ else
+ cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
+
+ writel(cr, imx->mmio_base + MX3_PWMCR);
+ } else if (cpu_is_mx1() || cpu_is_mx21()) {
+ /* The PWM subsystem allows for exact frequencies. However,
+ * I cannot connect a scope on my device to the PWM line and
+ * thus cannot provide the program the PWM controller
+ * exactly. Instead, I'm relying on the fact that the
+ * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+ * function group already. So I'll just modify the PWM sample
+ * register to follow the ratio of duty_ns vs. period_ns
+ * accordingly.
+ *
+ * This is good enough for programming the brightness of
+ * the LCD backlight.
+ *
+ * The real implementation would divide PERCLK[0] first by
+ * both the prescaler (/1 .. /128) and then by CLKSEL
+ * (/2 .. /16).
+ */
+ u32 max = readl(imx->mmio_base + MX1_PWMP);
+ u32 p = max * duty_ns / period_ns;
+ writel(max - p, imx->mmio_base + MX1_PWMS);
+ } else {
+ BUG();
+ }
+
+ return 0;
+}
+
+static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct imx_chip *imx = to_imx_chip(chip);
+ int rc = 0;
+
+ if (!imx->clk_enabled) {
+ rc = clk_prepare_enable(imx->clk);
+ if (!rc)
+ imx->clk_enabled = 1;
+ }
+ return rc;
+}
+
+static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct imx_chip *imx = to_imx_chip(chip);
+
+ writel(0, imx->mmio_base + MX3_PWMCR);
+
+ if (imx->clk_enabled) {
+ clk_disable_unprepare(imx->clk);
+ imx->clk_enabled = 0;
+ }
+}
+
+static struct pwm_ops imx_pwm_ops = {
+ .enable = imx_pwm_enable,
+ .disable = imx_pwm_disable,
+ .config = imx_pwm_config,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit imx_pwm_probe(struct platform_device *pdev)
+{
+ struct imx_chip *imx;
+ struct resource *r;
+ int ret = 0;
+
+ imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
+ if (imx == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ imx->clk = devm_clk_get(&pdev->dev, "pwm");
+
+ if (IS_ERR(imx->clk))
+ return PTR_ERR(imx->clk);
+
+ imx->chip.ops = &imx_pwm_ops;
+ imx->chip.dev = &pdev->dev;
+ imx->chip.base = -1;
+ imx->chip.npwm = 1;
+
+ imx->clk_enabled = 0;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ imx->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (imx->mmio_base == NULL)
+ return -EADDRNOTAVAIL;
+
+ ret = pwmchip_add(&imx->chip);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, imx);
+ return 0;
+}
+
+static int __devexit imx_pwm_remove(struct platform_device *pdev)
+{
+ struct imx_chip *imx;
+
+ imx = platform_get_drvdata(pdev);
+ if (imx == NULL)
+ return -ENODEV;
+
+ return pwmchip_remove(&imx->chip);
+}
+
+static struct platform_driver imx_pwm_driver = {
+ .driver = {
+ .name = "mxc_pwm",
+ },
+ .probe = imx_pwm_probe,
+ .remove = __devexit_p(imx_pwm_remove),
+};
+
+static int __init imx_pwm_init(void)
+{
+ return platform_driver_register(&imx_pwm_driver);
+}
+arch_initcall(imx_pwm_init);
+
+static void __exit imx_pwm_exit(void)
+{
+ platform_driver_unregister(&imx_pwm_driver);
+}
+module_exit(imx_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c
new file mode 100644
index 000000000000..adb87f0c1633
--- /dev/null
+++ b/drivers/pwm/pwm-lpc32xx.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@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; version 2.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct lpc32xx_pwm_chip {
+ struct pwm_chip chip;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+#define PWM_ENABLE (1 << 31)
+#define PWM_RELOADV(x) (((x) & 0xFF) << 8)
+#define PWM_DUTY(x) ((x) & 0xFF)
+
+#define to_lpc32xx_pwm_chip(_chip) \
+ container_of(_chip, struct lpc32xx_pwm_chip, chip)
+
+static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ unsigned long long c;
+ int period_cycles, duty_cycles;
+
+ c = clk_get_rate(lpc32xx->clk) / 256;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+
+ /* Handle high and low extremes */
+ if (c == 0)
+ c = 1;
+ if (c > 255)
+ c = 0; /* 0 set division by 256 */
+ period_cycles = c;
+
+ c = 256 * duty_ns;
+ do_div(c, period_ns);
+ duty_cycles = c;
+
+ writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles),
+ lpc32xx->base + (pwm->hwpwm << 2));
+
+ return 0;
+}
+
+static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+
+ return clk_enable(lpc32xx->clk);
+}
+
+static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+
+ writel(0, lpc32xx->base + (pwm->hwpwm << 2));
+ clk_disable(lpc32xx->clk);
+}
+
+static const struct pwm_ops lpc32xx_pwm_ops = {
+ .config = lpc32xx_pwm_config,
+ .enable = lpc32xx_pwm_enable,
+ .disable = lpc32xx_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int lpc32xx_pwm_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_pwm_chip *lpc32xx;
+ struct resource *res;
+ int ret;
+
+ lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL);
+ if (!lpc32xx)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ lpc32xx->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!lpc32xx->base)
+ return -EADDRNOTAVAIL;
+
+ lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(lpc32xx->clk))
+ return PTR_ERR(lpc32xx->clk);
+
+ lpc32xx->chip.dev = &pdev->dev;
+ lpc32xx->chip.ops = &lpc32xx_pwm_ops;
+ lpc32xx->chip.npwm = 2;
+
+ ret = pwmchip_add(&lpc32xx->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lpc32xx);
+
+ return 0;
+}
+
+static int __devexit lpc32xx_pwm_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev);
+
+ clk_disable(lpc32xx->clk);
+ return pwmchip_remove(&lpc32xx->chip);
+}
+
+static struct of_device_id lpc32xx_pwm_dt_ids[] = {
+ { .compatible = "nxp,lpc3220-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids);
+
+static struct platform_driver lpc32xx_pwm_driver = {
+ .driver = {
+ .name = "lpc32xx-pwm",
+ .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids),
+ },
+ .probe = lpc32xx_pwm_probe,
+ .remove = __devexit_p(lpc32xx_pwm_remove),
+};
+module_platform_driver(lpc32xx_pwm_driver);
+
+MODULE_ALIAS("platform:lpc32xx-pwm");
+MODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>");
+MODULE_DESCRIPTION("LPC32XX PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
new file mode 100644
index 000000000000..e5852646f082
--- /dev/null
+++ b/drivers/pwm/pwm-mxs.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/stmp_device.h>
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define PWM_CTRL 0x0
+#define PWM_ACTIVE0 0x10
+#define PWM_PERIOD0 0x20
+#define PERIOD_PERIOD(p) ((p) & 0xffff)
+#define PERIOD_PERIOD_MAX 0x10000
+#define PERIOD_ACTIVE_HIGH (3 << 16)
+#define PERIOD_INACTIVE_LOW (2 << 18)
+#define PERIOD_CDIV(div) (((div) & 0x7) << 20)
+#define PERIOD_CDIV_MAX 8
+
+struct mxs_pwm_chip {
+ struct pwm_chip chip;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
+
+static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+ int ret, div = 0;
+ unsigned int period_cycles, duty_cycles;
+ unsigned long rate;
+ unsigned long long c;
+
+ rate = clk_get_rate(mxs->clk);
+ while (1) {
+ c = rate / (1 << div);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ if (c < PERIOD_PERIOD_MAX)
+ break;
+ div++;
+ if (div > PERIOD_CDIV_MAX)
+ return -EINVAL;
+ }
+
+ period_cycles = c;
+ c *= duty_ns;
+ do_div(c, period_ns);
+ duty_cycles = c;
+
+ /*
+ * If the PWM channel is disabled, make sure to turn on the clock
+ * before writing the register. Otherwise, keep it enabled.
+ */
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ ret = clk_prepare_enable(mxs->clk);
+ if (ret)
+ return ret;
+ }
+
+ writel(duty_cycles << 16,
+ mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+ writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
+ PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
+ mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+ /*
+ * If the PWM is not enabled, turn the clock off again to save power.
+ */
+ if (!test_bit(PWMF_ENABLED, &pwm->flags))
+ clk_disable_unprepare(mxs->clk);
+
+ return 0;
+}
+
+static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+ int ret;
+
+ ret = clk_prepare_enable(mxs->clk);
+ if (ret)
+ return ret;
+
+ writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+
+ return 0;
+}
+
+static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+
+ writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+ clk_disable_unprepare(mxs->clk);
+}
+
+static const struct pwm_ops mxs_pwm_ops = {
+ .config = mxs_pwm_config,
+ .enable = mxs_pwm_enable,
+ .disable = mxs_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int mxs_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mxs_pwm_chip *mxs;
+ struct resource *res;
+ struct pinctrl *pinctrl;
+ int ret;
+
+ mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
+ if (!mxs)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mxs->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!mxs->base)
+ return -EADDRNOTAVAIL;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ return PTR_ERR(pinctrl);
+
+ mxs->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mxs->clk))
+ return PTR_ERR(mxs->clk);
+
+ mxs->chip.dev = &pdev->dev;
+ mxs->chip.ops = &mxs_pwm_ops;
+ mxs->chip.base = -1;
+ ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
+ return ret;
+ }
+
+ ret = pwmchip_add(&mxs->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
+ return ret;
+ }
+
+ mxs->dev = &pdev->dev;
+ platform_set_drvdata(pdev, mxs);
+
+ stmp_reset_block(mxs->base);
+
+ return 0;
+}
+
+static int __devexit mxs_pwm_remove(struct platform_device *pdev)
+{
+ struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev);
+
+ return pwmchip_remove(&mxs->chip);
+}
+
+static struct of_device_id mxs_pwm_dt_ids[] = {
+ { .compatible = "fsl,imx23-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
+
+static struct platform_driver mxs_pwm_driver = {
+ .driver = {
+ .name = "mxs-pwm",
+ .of_match_table = of_match_ptr(mxs_pwm_dt_ids),
+ },
+ .probe = mxs_pwm_probe,
+ .remove = __devexit_p(mxs_pwm_remove),
+};
+module_platform_driver(mxs_pwm_driver);
+
+MODULE_ALIAS("platform:mxs-pwm");
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale MXS PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
new file mode 100644
index 000000000000..bd5867a1c700
--- /dev/null
+++ b/drivers/pwm/pwm-pxa.c
@@ -0,0 +1,218 @@
+/*
+ * drivers/pwm/pwm-pxa.c
+ *
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * 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.
+ *
+ * 2008-02-13 initial version
+ * eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define HAS_SECONDARY_PWM 0x10
+#define PWM_ID_BASE(d) ((d) & 0xf)
+
+static const struct platform_device_id pwm_id_table[] = {
+ /* PWM has_secondary_pwm? */
+ { "pxa25x-pwm", 0 },
+ { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM },
+ { "pxa168-pwm", 1 },
+ { "pxa910-pwm", 1 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, pwm_id_table);
+
+/* PWM registers and bits definitions */
+#define PWMCR (0x00)
+#define PWMDCR (0x04)
+#define PWMPCR (0x08)
+
+#define PWMCR_SD (1 << 6)
+#define PWMDCR_FD (1 << 10)
+
+struct pxa_pwm_chip {
+ struct pwm_chip chip;
+ struct device *dev;
+
+ struct clk *clk;
+ int clk_enabled;
+ void __iomem *mmio_base;
+};
+
+static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct pxa_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ */
+static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, prescale, pv, dc;
+ unsigned long offset;
+ int rc;
+
+ if (period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ offset = pwm->hwpwm ? 0x10 : 0;
+
+ c = clk_get_rate(pc->clk);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+ prescale = (period_cycles - 1) / 1024;
+ pv = period_cycles / (prescale + 1) - 1;
+
+ if (prescale > 63)
+ return -EINVAL;
+
+ if (duty_ns == period_ns)
+ dc = PWMDCR_FD;
+ else
+ dc = (pv + 1) * duty_ns / period_ns;
+
+ /* NOTE: the clock to PWM has to be enabled first
+ * before writing to the registers
+ */
+ rc = clk_prepare_enable(pc->clk);
+ if (rc < 0)
+ return rc;
+
+ writel(prescale, pc->mmio_base + offset + PWMCR);
+ writel(dc, pc->mmio_base + offset + PWMDCR);
+ writel(pv, pc->mmio_base + offset + PWMPCR);
+
+ clk_disable_unprepare(pc->clk);
+ return 0;
+}
+
+static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+ int rc = 0;
+
+ if (!pc->clk_enabled) {
+ rc = clk_prepare_enable(pc->clk);
+ if (!rc)
+ pc->clk_enabled++;
+ }
+ return rc;
+}
+
+static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+
+ if (pc->clk_enabled) {
+ clk_disable_unprepare(pc->clk);
+ pc->clk_enabled--;
+ }
+}
+
+static struct pwm_ops pxa_pwm_ops = {
+ .config = pxa_pwm_config,
+ .enable = pxa_pwm_enable,
+ .disable = pxa_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct pxa_pwm_chip *pwm;
+ struct resource *r;
+ int ret = 0;
+
+ pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+ if (pwm == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pwm->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pwm->clk))
+ return PTR_ERR(pwm->clk);
+
+ pwm->clk_enabled = 0;
+
+ pwm->chip.dev = &pdev->dev;
+ pwm->chip.ops = &pxa_pwm_ops;
+ pwm->chip.base = -1;
+ pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (pwm->mmio_base == NULL)
+ return -EADDRNOTAVAIL;
+
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pwm);
+ return 0;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+ struct pxa_pwm_chip *chip;
+
+ chip = platform_get_drvdata(pdev);
+ if (chip == NULL)
+ return -ENODEV;
+
+ return pwmchip_remove(&chip->chip);
+}
+
+static struct platform_driver pwm_driver = {
+ .driver = {
+ .name = "pxa25x-pwm",
+ .owner = THIS_MODULE,
+ },
+ .probe = pwm_probe,
+ .remove = __devexit_p(pwm_remove),
+ .id_table = pwm_id_table,
+};
+
+static int __init pwm_init(void)
+{
+ return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+ platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
new file mode 100644
index 000000000000..d10386528c9c
--- /dev/null
+++ b/drivers/pwm/pwm-samsung.c
@@ -0,0 +1,356 @@
+/* drivers/pwm/pwm-samsung.c
+ *
+ * Copyright (c) 2007 Ben Dooks
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
+ *
+ * S3C series PWM device core
+ *
+ * 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.
+*/
+
+#define pr_fmt(fmt) "pwm-samsung: " fmt
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <mach/map.h>
+
+#include <plat/regs-timer.h>
+
+struct s3c_chip {
+ struct platform_device *pdev;
+
+ struct clk *clk_div;
+ struct clk *clk;
+ const char *label;
+
+ unsigned int period_ns;
+ unsigned int duty_ns;
+
+ unsigned char tcon_base;
+ unsigned char pwm_id;
+ struct pwm_chip chip;
+};
+
+#define to_s3c_chip(chip) container_of(chip, struct s3c_chip, chip)
+
+#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
+
+static struct clk *clk_scaler[2];
+
+static inline int pwm_is_tdiv(struct s3c_chip *chip)
+{
+ return clk_get_parent(chip->clk) == chip->clk_div;
+}
+
+#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0))
+#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2))
+#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3))
+#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1))
+
+static int s3c_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct s3c_chip *s3c = to_s3c_chip(chip);
+ unsigned long flags;
+ unsigned long tcon;
+
+ local_irq_save(flags);
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcon |= pwm_tcon_start(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void s3c_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct s3c_chip *s3c = to_s3c_chip(chip);
+ unsigned long flags;
+ unsigned long tcon;
+
+ local_irq_save(flags);
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcon &= ~pwm_tcon_start(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ local_irq_restore(flags);
+}
+
+static unsigned long pwm_calc_tin(struct s3c_chip *s3c, unsigned long freq)
+{
+ unsigned long tin_parent_rate;
+ unsigned int div;
+
+ tin_parent_rate = clk_get_rate(clk_get_parent(s3c->clk_div));
+ pwm_dbg(s3c, "tin parent at %lu\n", tin_parent_rate);
+
+ for (div = 2; div <= 16; div *= 2) {
+ if ((tin_parent_rate / (div << 16)) < freq)
+ return tin_parent_rate / div;
+ }
+
+ return tin_parent_rate / 16;
+}
+
+#define NS_IN_HZ (1000000000UL)
+
+static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct s3c_chip *s3c = to_s3c_chip(chip);
+ unsigned long tin_rate;
+ unsigned long tin_ns;
+ unsigned long period;
+ unsigned long flags;
+ unsigned long tcon;
+ unsigned long tcnt;
+ long tcmp;
+
+ /* We currently avoid using 64bit arithmetic by using the
+ * fact that anything faster than 1Hz is easily representable
+ * by 32bits. */
+
+ if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
+ return -ERANGE;
+
+ if (duty_ns > period_ns)
+ return -EINVAL;
+
+ if (period_ns == s3c->period_ns &&
+ duty_ns == s3c->duty_ns)
+ return 0;
+
+ /* The TCMP and TCNT can be read without a lock, they're not
+ * shared between the timers. */
+
+ tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id));
+ tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id));
+
+ period = NS_IN_HZ / period_ns;
+
+ pwm_dbg(s3c, "duty_ns=%d, period_ns=%d (%lu)\n",
+ duty_ns, period_ns, period);
+
+ /* Check to see if we are changing the clock rate of the PWM */
+
+ if (s3c->period_ns != period_ns) {
+ if (pwm_is_tdiv(s3c)) {
+ tin_rate = pwm_calc_tin(s3c, period);
+ clk_set_rate(s3c->clk_div, tin_rate);
+ } else
+ tin_rate = clk_get_rate(s3c->clk);
+
+ s3c->period_ns = period_ns;
+
+ pwm_dbg(s3c, "tin_rate=%lu\n", tin_rate);
+
+ tin_ns = NS_IN_HZ / tin_rate;
+ tcnt = period_ns / tin_ns;
+ } else
+ tin_ns = NS_IN_HZ / clk_get_rate(s3c->clk);
+
+ /* Note, counters count down */
+
+ tcmp = duty_ns / tin_ns;
+ tcmp = tcnt - tcmp;
+ /* the pwm hw only checks the compare register after a decrement,
+ so the pin never toggles if tcmp = tcnt */
+ if (tcmp == tcnt)
+ tcmp--;
+
+ pwm_dbg(s3c, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
+
+ if (tcmp < 0)
+ tcmp = 0;
+
+ /* Update the PWM register block. */
+
+ local_irq_save(flags);
+
+ __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id));
+ __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id));
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcon |= pwm_tcon_manulupdate(s3c);
+ tcon |= pwm_tcon_autoreload(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ tcon &= ~pwm_tcon_manulupdate(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static struct pwm_ops s3c_pwm_ops = {
+ .enable = s3c_pwm_enable,
+ .disable = s3c_pwm_disable,
+ .config = s3c_pwm_config,
+ .owner = THIS_MODULE,
+};
+
+static int s3c_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct s3c_chip *s3c;
+ unsigned long flags;
+ unsigned long tcon;
+ unsigned int id = pdev->id;
+ int ret;
+
+ if (id == 4) {
+ dev_err(dev, "TIMER4 is currently not supported\n");
+ return -ENXIO;
+ }
+
+ s3c = devm_kzalloc(&pdev->dev, sizeof(*s3c), GFP_KERNEL);
+ if (s3c == NULL) {
+ dev_err(dev, "failed to allocate pwm_device\n");
+ return -ENOMEM;
+ }
+
+ /* calculate base of control bits in TCON */
+ s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+ s3c->chip.ops = &s3c_pwm_ops;
+ s3c->chip.base = -1;
+ s3c->chip.npwm = 1;
+
+ s3c->clk = devm_clk_get(dev, "pwm-tin");
+ if (IS_ERR(s3c->clk)) {
+ dev_err(dev, "failed to get pwm tin clk\n");
+ return PTR_ERR(s3c->clk);
+ }
+
+ s3c->clk_div = devm_clk_get(dev, "pwm-tdiv");
+ if (IS_ERR(s3c->clk_div)) {
+ dev_err(dev, "failed to get pwm tdiv clk\n");
+ return PTR_ERR(s3c->clk_div);
+ }
+
+ clk_enable(s3c->clk);
+ clk_enable(s3c->clk_div);
+
+ local_irq_save(flags);
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcon |= pwm_tcon_invert(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ local_irq_restore(flags);
+
+ ret = pwmchip_add(&s3c->chip);
+ if (ret < 0) {
+ dev_err(dev, "failed to register pwm\n");
+ goto err_clk_tdiv;
+ }
+
+ pwm_dbg(s3c, "config bits %02x\n",
+ (__raw_readl(S3C2410_TCON) >> s3c->tcon_base) & 0x0f);
+
+ dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",
+ clk_get_rate(s3c->clk),
+ clk_get_rate(s3c->clk_div),
+ pwm_is_tdiv(s3c) ? "div" : "ext", s3c->tcon_base);
+
+ platform_set_drvdata(pdev, s3c);
+ return 0;
+
+ err_clk_tdiv:
+ clk_disable(s3c->clk_div);
+ clk_disable(s3c->clk);
+ return ret;
+}
+
+static int __devexit s3c_pwm_remove(struct platform_device *pdev)
+{
+ struct s3c_chip *s3c = platform_get_drvdata(pdev);
+ int err;
+
+ err = pwmchip_remove(&s3c->chip);
+ if (err < 0)
+ return err;
+
+ clk_disable(s3c->clk_div);
+ clk_disable(s3c->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct s3c_chip *s3c = platform_get_drvdata(pdev);
+
+ /* No one preserve these values during suspend so reset them
+ * Otherwise driver leaves PWM unconfigured if same values
+ * passed to pwm_config
+ */
+ s3c->period_ns = 0;
+ s3c->duty_ns = 0;
+
+ return 0;
+}
+
+static int s3c_pwm_resume(struct platform_device *pdev)
+{
+ struct s3c_chip *s3c = platform_get_drvdata(pdev);
+ unsigned long tcon;
+
+ /* Restore invertion */
+ tcon = __raw_readl(S3C2410_TCON);
+ tcon |= pwm_tcon_invert(s3c);
+ __raw_writel(tcon, S3C2410_TCON);
+
+ return 0;
+}
+
+#else
+#define s3c_pwm_suspend NULL
+#define s3c_pwm_resume NULL
+#endif
+
+static struct platform_driver s3c_pwm_driver = {
+ .driver = {
+ .name = "s3c24xx-pwm",
+ .owner = THIS_MODULE,
+ },
+ .probe = s3c_pwm_probe,
+ .remove = __devexit_p(s3c_pwm_remove),
+ .suspend = s3c_pwm_suspend,
+ .resume = s3c_pwm_resume,
+};
+
+static int __init pwm_init(void)
+{
+ int ret;
+
+ clk_scaler[0] = clk_get(NULL, "pwm-scaler0");
+ clk_scaler[1] = clk_get(NULL, "pwm-scaler1");
+
+ if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {
+ pr_err("failed to get scaler clocks\n");
+ return -EINVAL;
+ }
+
+ ret = platform_driver_register(&s3c_pwm_driver);
+ if (ret)
+ pr_err("failed to add pwm driver\n");
+
+ return ret;
+}
+
+arch_initcall(pwm_init);
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
new file mode 100644
index 000000000000..02ce18d5e49a
--- /dev/null
+++ b/drivers/pwm/pwm-tegra.c
@@ -0,0 +1,261 @@
+/*
+ * drivers/pwm/pwm-tegra.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * 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 <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PWM_ENABLE (1 << 31)
+#define PWM_DUTY_WIDTH 8
+#define PWM_DUTY_SHIFT 16
+#define PWM_SCALE_WIDTH 13
+#define PWM_SCALE_SHIFT 0
+
+#define NUM_PWM 4
+
+struct tegra_pwm_chip {
+ struct pwm_chip chip;
+ struct device *dev;
+
+ struct clk *clk;
+
+ void __iomem *mmio_base;
+};
+
+static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct tegra_pwm_chip, chip);
+}
+
+static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num)
+{
+ return readl(chip->mmio_base + (num << 4));
+}
+
+static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num,
+ unsigned long val)
+{
+ writel(val, chip->mmio_base + (num << 4));
+}
+
+static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long rate, hz;
+ u32 val = 0;
+ int err;
+
+ /*
+ * Convert from duty_ns / period_ns to a fixed number of duty ticks
+ * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
+ * nearest integer during division.
+ */
+ c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2;
+ do_div(c, period_ns);
+
+ val = (u32)c << PWM_DUTY_SHIFT;
+
+ /*
+ * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+ * cycles at the PWM clock rate will take period_ns nanoseconds.
+ */
+ rate = clk_get_rate(pc->clk) >> PWM_DUTY_WIDTH;
+ hz = 1000000000ul / period_ns;
+
+ rate = (rate + (hz / 2)) / hz;
+
+ /*
+ * Since the actual PWM divider is the register's frequency divider
+ * field minus 1, we need to decrement to get the correct value to
+ * write to the register.
+ */
+ if (rate > 0)
+ rate--;
+
+ /*
+ * Make sure that the rate will fit in the register's frequency
+ * divider field.
+ */
+ if (rate >> PWM_SCALE_WIDTH)
+ return -EINVAL;
+
+ val |= rate << PWM_SCALE_SHIFT;
+
+ /*
+ * If the PWM channel is disabled, make sure to turn on the clock
+ * before writing the register. Otherwise, keep it enabled.
+ */
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ err = clk_prepare_enable(pc->clk);
+ if (err < 0)
+ return err;
+ } else
+ val |= PWM_ENABLE;
+
+ pwm_writel(pc, pwm->hwpwm, val);
+
+ /*
+ * If the PWM is not enabled, turn the clock off again to save power.
+ */
+ if (!test_bit(PWMF_ENABLED, &pwm->flags))
+ clk_disable_unprepare(pc->clk);
+
+ return 0;
+}
+
+static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ int rc = 0;
+ u32 val;
+
+ rc = clk_prepare_enable(pc->clk);
+ if (rc < 0)
+ return rc;
+
+ val = pwm_readl(pc, pwm->hwpwm);
+ val |= PWM_ENABLE;
+ pwm_writel(pc, pwm->hwpwm, val);
+
+ return 0;
+}
+
+static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ u32 val;
+
+ val = pwm_readl(pc, pwm->hwpwm);
+ val &= ~PWM_ENABLE;
+ pwm_writel(pc, pwm->hwpwm, val);
+
+ clk_disable_unprepare(pc->clk);
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+ .config = tegra_pwm_config,
+ .enable = tegra_pwm_enable,
+ .disable = tegra_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+ struct tegra_pwm_chip *pwm;
+ struct resource *r;
+ int ret;
+
+ pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+ if (!pwm) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pwm->dev = &pdev->dev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no memory resources defined\n");
+ return -ENODEV;
+ }
+
+ pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pwm->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() region\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ platform_set_drvdata(pdev, pwm);
+
+ pwm->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pwm->clk))
+ return PTR_ERR(pwm->clk);
+
+ pwm->chip.dev = &pdev->dev;
+ pwm->chip.ops = &tegra_pwm_ops;
+ pwm->chip.base = -1;
+ pwm->chip.npwm = NUM_PWM;
+
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit tegra_pwm_remove(struct platform_device *pdev)
+{
+ struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
+ int i;
+
+ if (WARN_ON(!pc))
+ return -ENODEV;
+
+ for (i = 0; i < NUM_PWM; i++) {
+ struct pwm_device *pwm = &pc->chip.pwms[i];
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags))
+ if (clk_prepare_enable(pc->clk) < 0)
+ continue;
+
+ pwm_writel(pc, i, 0);
+
+ clk_disable_unprepare(pc->clk);
+ }
+
+ return pwmchip_remove(&pc->chip);
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id tegra_pwm_of_match[] = {
+ { .compatible = "nvidia,tegra20-pwm" },
+ { .compatible = "nvidia,tegra30-pwm" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
+#endif
+
+static struct platform_driver tegra_pwm_driver = {
+ .driver = {
+ .name = "tegra-pwm",
+ .of_match_table = of_match_ptr(tegra_pwm_of_match),
+ },
+ .probe = tegra_pwm_probe,
+ .remove = __devexit_p(tegra_pwm_remove),
+};
+
+module_platform_driver(tegra_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_ALIAS("platform:tegra-pwm");
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
new file mode 100644
index 000000000000..3c2ad284ee3e
--- /dev/null
+++ b/drivers/pwm/pwm-tiecap.c
@@ -0,0 +1,232 @@
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+
+/* ECAP registers and bits definitions */
+#define CAP1 0x08
+#define CAP2 0x0C
+#define CAP3 0x10
+#define CAP4 0x14
+#define ECCTL2 0x2A
+#define ECCTL2_APWM_MODE BIT(9)
+#define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
+#define ECCTL2_TSCTR_FREERUN BIT(4)
+
+struct ecap_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+};
+
+static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ecap_pwm_chip, chip);
+}
+
+/*
+ * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
+ * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
+ */
+static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned int reg_val;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ pm_runtime_get_sync(pc->chip.dev);
+
+ reg_val = readw(pc->mmio_base + ECCTL2);
+
+ /* Configure APWM mode & disable sync option */
+ reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
+
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ /* Update active registers if not running */
+ writel(duty_cycles, pc->mmio_base + CAP2);
+ writel(period_cycles, pc->mmio_base + CAP1);
+ } else {
+ /*
+ * Update shadow registers to configure period and
+ * compare values. This helps current PWM period to
+ * complete on reconfiguring
+ */
+ writel(duty_cycles, pc->mmio_base + CAP4);
+ writel(period_cycles, pc->mmio_base + CAP3);
+ }
+
+ pm_runtime_put_sync(pc->chip.dev);
+ return 0;
+}
+
+static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(pc->chip.dev);
+
+ /*
+ * Enable 'Free run Time stamp counter mode' to start counter
+ * and 'APWM mode' to enable APWM output
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
+ writew(reg_val, pc->mmio_base + ECCTL2);
+ return 0;
+}
+
+static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ unsigned int reg_val;
+
+ /*
+ * Disable 'Free run Time stamp counter mode' to stop counter
+ * and 'APWM mode' to put APWM output to low
+ */
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
+ writew(reg_val, pc->mmio_base + ECCTL2);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(pc->chip.dev);
+}
+
+static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static const struct pwm_ops ecap_pwm_ops = {
+ .free = ecap_pwm_free,
+ .config = ecap_pwm_config,
+ .enable = ecap_pwm_enable,
+ .disable = ecap_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ecap_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ecap_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ecap_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = 1;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ return 0;
+}
+
+static int __devexit ecap_pwm_remove(struct platform_device *pdev)
+{
+ struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return pwmchip_remove(&pc->chip);
+}
+
+static struct platform_driver ecap_pwm_driver = {
+ .driver = {
+ .name = "ecap",
+ },
+ .probe = ecap_pwm_probe,
+ .remove = __devexit_p(ecap_pwm_remove),
+};
+
+module_platform_driver(ecap_pwm_driver);
+
+MODULE_DESCRIPTION("ECAP PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
new file mode 100644
index 000000000000..010d232cb0c8
--- /dev/null
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -0,0 +1,411 @@
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+/* EHRPWM registers and bits definitions */
+
+/* Time base module registers */
+#define TBCTL 0x00
+#define TBPRD 0x0A
+
+#define TBCTL_RUN_MASK (BIT(15) | BIT(14))
+#define TBCTL_STOP_NEXT 0
+#define TBCTL_STOP_ON_CYCLE BIT(14)
+#define TBCTL_FREE_RUN (BIT(15) | BIT(14))
+#define TBCTL_PRDLD_MASK BIT(3)
+#define TBCTL_PRDLD_SHDW 0
+#define TBCTL_PRDLD_IMDT BIT(3)
+#define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
+ BIT(8) | BIT(7))
+#define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0))
+#define TBCTL_CTRMODE_UP 0
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0))
+
+#define TBCTL_HSPCLKDIV_SHIFT 7
+#define TBCTL_CLKDIV_SHIFT 10
+
+#define CLKDIV_MAX 7
+#define HSPCLKDIV_MAX 7
+#define PERIOD_MAX 0xFFFF
+
+/* compare module registers */
+#define CMPA 0x12
+#define CMPB 0x14
+
+/* Action qualifier module registers */
+#define AQCTLA 0x16
+#define AQCTLB 0x18
+#define AQSFRC 0x1A
+#define AQCSFRC 0x1C
+
+#define AQCTL_CBU_MASK (BIT(9) | BIT(8))
+#define AQCTL_CBU_FRCLOW BIT(8)
+#define AQCTL_CBU_FRCHIGH BIT(9)
+#define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8))
+#define AQCTL_CAU_MASK (BIT(5) | BIT(4))
+#define AQCTL_CAU_FRCLOW BIT(4)
+#define AQCTL_CAU_FRCHIGH BIT(5)
+#define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4))
+#define AQCTL_PRD_MASK (BIT(3) | BIT(2))
+#define AQCTL_PRD_FRCLOW BIT(2)
+#define AQCTL_PRD_FRCHIGH BIT(3)
+#define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2))
+#define AQCTL_ZRO_MASK (BIT(1) | BIT(0))
+#define AQCTL_ZRO_FRCLOW BIT(0)
+#define AQCTL_ZRO_FRCHIGH BIT(1)
+#define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0))
+
+#define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6))
+#define AQSFRC_RLDCSF_ZRO 0
+#define AQSFRC_RLDCSF_PRD BIT(6)
+#define AQSFRC_RLDCSF_ZROPRD BIT(7)
+#define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6))
+
+#define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2))
+#define AQCSFRC_CSFB_FRCDIS 0
+#define AQCSFRC_CSFB_FRCLOW BIT(2)
+#define AQCSFRC_CSFB_FRCHIGH BIT(3)
+#define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2))
+#define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0))
+#define AQCSFRC_CSFA_FRCDIS 0
+#define AQCSFRC_CSFA_FRCLOW BIT(0)
+#define AQCSFRC_CSFA_FRCHIGH BIT(1)
+#define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0))
+
+#define NUM_PWM_CHANNEL 2 /* EHRPWM channels */
+
+struct ehrpwm_pwm_chip {
+ struct pwm_chip chip;
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+};
+
+static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ehrpwm_pwm_chip, chip);
+}
+
+static void ehrpwm_write(void *base, int offset, unsigned int val)
+{
+ writew(val & 0xFFFF, base + offset);
+}
+
+static void ehrpwm_modify(void *base, int offset,
+ unsigned short mask, unsigned short val)
+{
+ unsigned short regval;
+
+ regval = readw(base + offset);
+ regval &= ~mask;
+ regval |= val & mask;
+ writew(regval, base + offset);
+}
+
+/**
+ * set_prescale_div - Set up the prescaler divider function
+ * @rqst_prescaler: prescaler value min
+ * @prescale_div: prescaler value set
+ * @tb_clk_div: Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler,
+ unsigned short *prescale_div, unsigned short *tb_clk_div)
+{
+ unsigned int clkdiv, hspclkdiv;
+
+ for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
+ for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
+
+ /*
+ * calculations for prescaler value :
+ * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+ * HSPCLKDIVIDER = 2 ** hspclkdiv
+ * CLKDIVIDER = (1), if clkdiv == 0 *OR*
+ * (2 * clkdiv), if clkdiv != 0
+ *
+ * Configure prescale_div value such that period
+ * register value is less than 65535.
+ */
+
+ *prescale_div = (1 << clkdiv) *
+ (hspclkdiv ? (hspclkdiv * 2) : 1);
+ if (*prescale_div > rqst_prescaler) {
+ *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
+ (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan,
+ unsigned long duty_cycles)
+{
+ int cmp_reg, aqctl_reg;
+ unsigned short aqctl_val, aqctl_mask;
+
+ /*
+ * Channels can be configured from action qualifier module.
+ * Channel 0 configured with compare A register and for
+ * up-counter mode.
+ * Channel 1 configured with compare B register and for
+ * up-counter mode.
+ */
+ if (chan == 1) {
+ aqctl_reg = AQCTLB;
+ cmp_reg = CMPB;
+ /* Configure PWM Low from compare B value */
+ aqctl_val = AQCTL_CBU_FRCLOW;
+ aqctl_mask = AQCTL_CBU_MASK;
+ } else {
+ cmp_reg = CMPA;
+ aqctl_reg = AQCTLA;
+ /* Configure PWM Low from compare A value*/
+ aqctl_val = AQCTL_CAU_FRCLOW;
+ aqctl_mask = AQCTL_CAU_MASK;
+ }
+
+ /* Configure PWM High from period value and zero value */
+ aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH;
+ aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
+ ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val);
+
+ ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles);
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles;
+ unsigned short ps_divval, tb_divval;
+
+ if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+ }
+
+ /* Configure clock prescaler to support Low frequency PWM wave */
+ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
+ &tb_divval)) {
+ dev_err(chip->dev, "Unsupported values\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(chip->dev);
+
+ /* Update clock prescaler values */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+
+ /* Update period & duty cycle with presacler division */
+ period_cycles = period_cycles / ps_divval;
+ duty_cycles = duty_cycles / ps_divval;
+
+ /* Configure shadow loading on Period register */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW);
+
+ ehrpwm_write(pc->mmio_base, TBPRD, period_cycles);
+
+ /* Configure ehrpwm counter for up-count mode */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
+ TBCTL_CTRMODE_UP);
+
+ /* Configure the channel for duty cycle */
+ configure_chans(pc, pwm->hwpwm, duty_cycles);
+ pm_runtime_put_sync(chip->dev);
+ return 0;
+}
+
+static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(chip->dev);
+
+ /* Disabling Action Qualifier on PWM output */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /* Changes to shadow mode */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_ZRO);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Enable time counter for free_run */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN);
+ return 0;
+}
+
+static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ unsigned short aqcsfrc_val, aqcsfrc_mask;
+
+ /* Action Qualifier puts PWM output low forcefully */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /*
+ * Changes to immediate action on Action Qualifier. This puts
+ * Action Qualifier control on PWM output from next TBCLK
+ */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_IMDT);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Stop Time base counter */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(chip->dev);
+}
+
+static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ if (test_bit(PWMF_ENABLED, &pwm->flags)) {
+ dev_warn(chip->dev, "Removing PWM device without disabling\n");
+ pm_runtime_put_sync(chip->dev);
+ }
+}
+
+static const struct pwm_ops ehrpwm_pwm_ops = {
+ .free = ehrpwm_pwm_free,
+ .config = ehrpwm_pwm_config,
+ .enable = ehrpwm_pwm_enable,
+ .disable = ehrpwm_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct clk *clk;
+ struct ehrpwm_pwm_chip *pc;
+
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ pc->chip.dev = &pdev->dev;
+ pc->chip.ops = &ehrpwm_pwm_ops;
+ pc->chip.base = -1;
+ pc->chip.npwm = NUM_PWM_CHANNEL;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!pc->mmio_base) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ ret = pwmchip_add(&pc->chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ platform_set_drvdata(pdev, pc);
+ return 0;
+}
+
+static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev)
+{
+ struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return pwmchip_remove(&pc->chip);
+}
+
+static struct platform_driver ehrpwm_pwm_driver = {
+ .driver = {
+ .name = "ehrpwm",
+ },
+ .probe = ehrpwm_pwm_probe,
+ .remove = __devexit_p(ehrpwm_pwm_remove),
+};
+
+module_platform_driver(ehrpwm_pwm_driver);
+
+MODULE_DESCRIPTION("EHRPWM PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
new file mode 100644
index 000000000000..548021439f0c
--- /dev/null
+++ b/drivers/pwm/pwm-vt8500.c
@@ -0,0 +1,177 @@
+/*
+ * drivers/pwm/pwm-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#define VT8500_NR_PWMS 4
+
+struct vt8500_chip {
+ struct pwm_chip chip;
+ void __iomem *base;
+};
+
+#define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip)
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
+{
+ int loops = msecs_to_loops(10);
+ while ((readb(reg) & bitmask) && --loops)
+ cpu_relax();
+
+ if (unlikely(!loops))
+ pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+ bitmask);
+}
+
+static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, prescale, pv, dc;
+
+ c = 25000000/2; /* wild guess --- need to implement clocks */
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+ prescale = (period_cycles - 1) / 4096;
+ pv = period_cycles / (prescale + 1) - 1;
+ if (pv > 4095)
+ pv = 4095;
+
+ if (prescale > 1023)
+ return -EINVAL;
+
+ c = (unsigned long long)pv * duty_ns;
+ do_div(c, period_ns);
+ dc = c;
+
+ pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1));
+ writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4));
+
+ pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2));
+ writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4));
+
+ pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3));
+ writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4));
+
+ return 0;
+}
+
+static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+
+ pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+ writel(5, vt8500->base + (pwm->hwpwm << 4));
+ return 0;
+}
+
+static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+
+ pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0));
+ writel(0, vt8500->base + (pwm->hwpwm << 4));
+}
+
+static struct pwm_ops vt8500_pwm_ops = {
+ .enable = vt8500_pwm_enable,
+ .disable = vt8500_pwm_disable,
+ .config = vt8500_pwm_config,
+ .owner = THIS_MODULE,
+};
+
+static int __devinit pwm_probe(struct platform_device *pdev)
+{
+ struct vt8500_chip *chip;
+ struct resource *r;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ chip->chip.dev = &pdev->dev;
+ chip->chip.ops = &vt8500_pwm_ops;
+ chip->chip.base = -1;
+ chip->chip.npwm = VT8500_NR_PWMS;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ return -ENODEV;
+ }
+
+ chip->base = devm_request_and_ioremap(&pdev->dev, r);
+ if (chip->base == NULL)
+ return -EADDRNOTAVAIL;
+
+ ret = pwmchip_add(&chip->chip);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, chip);
+ return ret;
+}
+
+static int __devexit pwm_remove(struct platform_device *pdev)
+{
+ struct vt8500_chip *chip;
+
+ chip = platform_get_drvdata(pdev);
+ if (chip == NULL)
+ return -ENODEV;
+
+ return pwmchip_remove(&chip->chip);
+}
+
+static struct platform_driver pwm_driver = {
+ .driver = {
+ .name = "vt8500-pwm",
+ .owner = THIS_MODULE,
+ },
+ .probe = pwm_probe,
+ .remove = __devexit_p(pwm_remove),
+};
+
+static int __init pwm_init(void)
+{
+ return platform_driver_register(&pwm_driver);
+}
+arch_initcall(pwm_init);
+
+static void __exit pwm_exit(void)
+{
+ platform_driver_unregister(&pwm_driver);
+}
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f34c3be6c9fe..4e932cc695e9 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -272,7 +272,7 @@ config REGULATOR_S2MPS11
config REGULATOR_S5M8767
tristate "Samsung S5M8767A voltage regulator"
- depends on MFD_S5M_CORE
+ depends on MFD_SEC_CORE
help
This driver supports a Samsung S5M8767A voltage output regulator
via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 13d424fc1c14..10f2f4d4d190 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -848,18 +848,12 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_regulator_match[] = {
- { .compatible = "stericsson,ab8500-regulator", },
- {}
-};
-
static struct platform_driver ab8500_regulator_driver = {
.probe = ab8500_regulator_probe,
.remove = __devexit_p(ab8500_regulator_remove),
.driver = {
.name = "ab8500-regulator",
.owner = THIS_MODULE,
- .of_match_table = ab8500_regulator_match,
},
};
diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c
index 9dbb491b6efa..359f8d18fc3f 100644
--- a/drivers/regulator/db8500-prcmu.c
+++ b/drivers/regulator/db8500-prcmu.c
@@ -547,16 +547,10 @@ static int __exit db8500_regulator_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id db8500_prcmu_regulator_match[] = {
- { .compatible = "stericsson,db8500-prcmu-regulator", },
- {}
-};
-
static struct platform_driver db8500_regulator_driver = {
.driver = {
.name = "db8500-prcmu-regulators",
.owner = THIS_MODULE,
- .of_match_table = db8500_prcmu_regulator_match,
},
.probe = db8500_regulator_probe,
.remove = __exit_p(db8500_regulator_remove),
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index 102287fa7ecb..abe64a32aedf 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -19,15 +19,15 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
-#include <linux/mfd/s5m87xx/s5m-core.h>
-#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s5m8767.h>
struct s5m8767_info {
struct device *dev;
- struct s5m87xx_dev *iodev;
+ struct sec_pmic_dev *iodev;
int num_regulators;
struct regulator_dev **rdev;
- struct s5m_opmode_data *opmode;
+ struct sec_opmode_data *opmode;
int ramp_delay;
bool buck2_ramp;
@@ -45,43 +45,43 @@ struct s5m8767_info {
int buck_gpioindex;
};
-struct s5m_voltage_desc {
+struct sec_voltage_desc {
int max;
int min;
int step;
};
-static const struct s5m_voltage_desc buck_voltage_val1 = {
+static const struct sec_voltage_desc buck_voltage_val1 = {
.max = 2225000,
.min = 650000,
.step = 6250,
};
-static const struct s5m_voltage_desc buck_voltage_val2 = {
+static const struct sec_voltage_desc buck_voltage_val2 = {
.max = 1600000,
.min = 600000,
.step = 6250,
};
-static const struct s5m_voltage_desc buck_voltage_val3 = {
+static const struct sec_voltage_desc buck_voltage_val3 = {
.max = 3000000,
.min = 750000,
.step = 12500,
};
-static const struct s5m_voltage_desc ldo_voltage_val1 = {
+static const struct sec_voltage_desc ldo_voltage_val1 = {
.max = 3950000,
.min = 800000,
.step = 50000,
};
-static const struct s5m_voltage_desc ldo_voltage_val2 = {
+static const struct sec_voltage_desc ldo_voltage_val2 = {
.max = 2375000,
.min = 800000,
.step = 25000,
};
-static const struct s5m_voltage_desc *reg_voltage_map[] = {
+static const struct sec_voltage_desc *reg_voltage_map[] = {
[S5M8767_LDO1] = &ldo_voltage_val2,
[S5M8767_LDO2] = &ldo_voltage_val2,
[S5M8767_LDO3] = &ldo_voltage_val1,
@@ -213,7 +213,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
else if (ret)
return ret;
- ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+ ret = sec_reg_read(s5m8767->iodev, reg, &val);
if (ret)
return ret;
@@ -230,7 +230,7 @@ static int s5m8767_reg_enable(struct regulator_dev *rdev)
if (ret)
return ret;
- return s5m_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
+ return sec_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
}
static int s5m8767_reg_disable(struct regulator_dev *rdev)
@@ -243,7 +243,7 @@ static int s5m8767_reg_disable(struct regulator_dev *rdev)
if (ret)
return ret;
- return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask);
+ return sec_reg_update(s5m8767->iodev, reg, ~mask, mask);
}
static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
@@ -305,7 +305,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
mask = (reg_id < S5M8767_BUCK1) ? 0x3f : 0xff;
- ret = s5m_reg_read(s5m8767->iodev, reg, &val);
+ ret = sec_reg_read(s5m8767->iodev, reg, &val);
if (ret)
return ret;
@@ -315,7 +315,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
}
static int s5m8767_convert_voltage_to_sel(
- const struct s5m_voltage_desc *desc,
+ const struct sec_voltage_desc *desc,
int min_vol, int max_vol)
{
int selector = 0;
@@ -407,7 +407,7 @@ static int s5m8767_set_voltage_sel(struct regulator_dev *rdev,
if (ret)
return ret;
- return s5m_reg_update(s5m8767->iodev, reg, selector, mask);
+ return sec_reg_update(s5m8767->iodev, reg, selector, mask);
}
}
@@ -416,7 +416,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int new_sel)
{
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
- const struct s5m_voltage_desc *desc;
+ const struct sec_voltage_desc *desc;
int reg_id = rdev_get_id(rdev);
desc = reg_voltage_map[reg_id];
@@ -501,8 +501,8 @@ static struct regulator_desc regulators[] = {
static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
{
- struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
struct regulator_config config = { };
struct regulator_dev **rdev;
struct s5m8767_info *s5m8767;
@@ -572,21 +572,21 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
pdata->buck2_init +
buck_voltage_val2.step);
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init);
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init);
buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
pdata->buck3_init,
pdata->buck3_init +
buck_voltage_val2.step);
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init);
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init);
buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
pdata->buck4_init,
pdata->buck4_init +
buck_voltage_val2.step);
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init);
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init);
for (i = 0; i < 8; i++) {
if (s5m8767->buck2_gpiodvs) {
@@ -671,13 +671,13 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
pdata->buck4_gpiodvs) {
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
(pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
(pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
(pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
}
@@ -685,61 +685,61 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
/* Initialize GPIO DVS registers */
for (i = 0; i < 8; i++) {
if (s5m8767->buck2_gpiodvs) {
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
s5m8767->buck2_vol[i]);
}
if (s5m8767->buck3_gpiodvs) {
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
s5m8767->buck3_vol[i]);
}
if (s5m8767->buck4_gpiodvs) {
- s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
+ sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
s5m8767->buck4_vol[i]);
}
}
if (s5m8767->buck2_ramp)
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
if (s5m8767->buck3_ramp)
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
if (s5m8767->buck4_ramp)
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
if (s5m8767->buck2_ramp || s5m8767->buck3_ramp
|| s5m8767->buck4_ramp) {
switch (s5m8767->ramp_delay) {
case 5:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0x40, 0xf0);
break;
case 10:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0x90, 0xf0);
break;
case 25:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0xd0, 0xf0);
break;
case 50:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0xe0, 0xf0);
break;
case 100:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0xf0, 0xf0);
break;
default:
- s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
+ sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
0x90, 0xf0);
}
}
for (i = 0; i < pdata->num_regulators; i++) {
- const struct s5m_voltage_desc *desc;
+ const struct sec_voltage_desc *desc;
int id = pdata->regulators[i].id;
desc = reg_voltage_map[id];
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 08cbdb900a18..fabc99a75c65 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -135,6 +135,16 @@ config RTC_DRV_88PM860X
This driver can also be built as a module. If so, the module
will be called rtc-88pm860x.
+config RTC_DRV_88PM80X
+ tristate "Marvell 88PM80x"
+ depends on RTC_CLASS && I2C && MFD_88PM800
+ help
+ If you say yes here you get support for RTC function in Marvell
+ 88PM80x chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-88pm80x.
+
config RTC_DRV_DS1307
tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
help
@@ -694,6 +704,7 @@ config RTC_DRV_AB3100
config RTC_DRV_AB8500
tristate "ST-Ericsson AB8500 RTC"
depends on AB8500_CORE
+ select RTC_INTF_DEV_UIE_EMUL
help
Select this to enable the ST-Ericsson AB8500 power management IC RTC
support. This chip contains a battery- and capacitor-backed RTC.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2973921c30d8..0d5b2b66f90d 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -16,6 +16,7 @@ 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_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
new file mode 100644
index 000000000000..6367984e0565
--- /dev/null
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -0,0 +1,369 @@
+/*
+ * Real Time Clock driver for Marvell 88PM80x PMIC
+ *
+ * Copyright (c) 2012 Marvell International Ltd.
+ * Wenzeng Chen<wzch@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/rtc.h>
+
+#define PM800_RTC_COUNTER1 (0xD1)
+#define PM800_RTC_COUNTER2 (0xD2)
+#define PM800_RTC_COUNTER3 (0xD3)
+#define PM800_RTC_COUNTER4 (0xD4)
+#define PM800_RTC_EXPIRE1_1 (0xD5)
+#define PM800_RTC_EXPIRE1_2 (0xD6)
+#define PM800_RTC_EXPIRE1_3 (0xD7)
+#define PM800_RTC_EXPIRE1_4 (0xD8)
+#define PM800_RTC_TRIM1 (0xD9)
+#define PM800_RTC_TRIM2 (0xDA)
+#define PM800_RTC_TRIM3 (0xDB)
+#define PM800_RTC_TRIM4 (0xDC)
+#define PM800_RTC_EXPIRE2_1 (0xDD)
+#define PM800_RTC_EXPIRE2_2 (0xDE)
+#define PM800_RTC_EXPIRE2_3 (0xDF)
+#define PM800_RTC_EXPIRE2_4 (0xE0)
+
+#define PM800_POWER_DOWN_LOG1 (0xE5)
+#define PM800_POWER_DOWN_LOG2 (0xE6)
+
+struct pm80x_rtc_info {
+ struct pm80x_chip *chip;
+ struct regmap *map;
+ struct rtc_device *rtc_dev;
+ struct device *dev;
+ struct delayed_work calib_work;
+
+ int irq;
+ int vrtc;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data;
+ int mask;
+
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN,
+ mask);
+ rtc_update_irq(info->rtc_dev, 1, RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ regmap_update_bits(info->map, PM800_RTC_CONTROL,
+ PM800_ALARM1_EN, PM800_ALARM1_EN);
+ else
+ regmap_update_bits(info->map, PM800_RTC_CONTROL,
+ PM800_ALARM1_EN, 0);
+ return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+ struct rtc_time *alrm)
+{
+ unsigned long next_time;
+ unsigned long now_time;
+
+ next->tm_year = now->tm_year;
+ next->tm_mon = now->tm_mon;
+ next->tm_mday = now->tm_mday;
+ next->tm_hour = alrm->tm_hour;
+ next->tm_min = alrm->tm_min;
+ next->tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(now, &now_time);
+ rtc_tm_to_time(next, &next_time);
+
+ if (next_time < now_time) {
+ /* Advance one day */
+ next_time += 60 * 60 * 24;
+ rtc_time_to_tm(next_time, next);
+ }
+}
+
+static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+ rtc_time_to_tm(ticks, tm);
+ return 0;
+}
+
+static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ dev_dbg(info->dev,
+ "Set time %d out of range. Please set time between 1970 to 2038.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ rtc_tm_to_time(tm, &ticks);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ticks - data;
+ dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+ buf[0] = base & 0xFF;
+ buf[1] = (base >> 8) & 0xFF;
+ buf[2] = (base >> 16) & 0xFF;
+ buf[3] = (base >> 24) & 0xFF;
+ regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+
+ return 0;
+}
+
+static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ int ret;
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &alrm->time);
+ regmap_read(info->map, PM800_RTC_CONTROL, &ret);
+ alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0;
+ alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0;
+ return 0;
+}
+
+static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ struct rtc_time now_tm, alarm_tm;
+ unsigned long ticks, base, data;
+ unsigned char buf[4];
+ int mask;
+
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &now_tm);
+ dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks);
+ rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+ /* get new ticks for alarm in 24 hours */
+ rtc_tm_to_time(&alarm_tm, &ticks);
+ dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks);
+ data = ticks - base;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+ buf[2] = (data >> 16) & 0xff;
+ buf[3] = (data >> 24) & 0xff;
+ regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+ if (alrm->enabled) {
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask);
+ } else {
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask,
+ PM800_ALARM | PM800_ALARM_WAKEUP);
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops pm80x_rtc_ops = {
+ .read_time = pm80x_rtc_read_time,
+ .set_time = pm80x_rtc_set_time,
+ .read_alarm = pm80x_rtc_read_alarm,
+ .set_alarm = pm80x_rtc_set_alarm,
+ .alarm_irq_enable = pm80x_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM
+static int pm80x_rtc_suspend(struct device *dev)
+{
+ return pm80x_dev_suspend(dev);
+}
+
+static int pm80x_rtc_resume(struct device *dev)
+{
+ return pm80x_dev_resume(dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume);
+
+static int __devinit pm80x_rtc_probe(struct platform_device *pdev)
+{
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_platform_data *pm80x_pdata;
+ struct pm80x_rtc_pdata *pdata = NULL;
+ struct pm80x_rtc_info *info;
+ struct rtc_time tm;
+ unsigned long ticks = 0;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL)
+ dev_warn(&pdev->dev, "No platform data!\n");
+
+ info =
+ devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->chip = chip;
+ info->map = chip->regmap;
+ if (!info->map) {
+ dev_err(&pdev->dev, "no regmap!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ ret = pm80x_request_irq(chip, info->irq, rtc_update_handler,
+ IRQF_ONESHOT, "rtc", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ goto out;
+ }
+
+ ret = pm80x_rtc_read_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial time.\n");
+ goto out_rtc;
+ }
+ if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
+ tm.tm_year = 70;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ ret = pm80x_rtc_set_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set initial time.\n");
+ goto out_rtc;
+ }
+ }
+ rtc_tm_to_time(&tm, &ticks);
+
+ info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev,
+ &pm80x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+ /*
+ * enable internal XO instead of internal 3.25MHz clock since it can
+ * free running in PMIC power-down state.
+ */
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO,
+ PM800_RTC1_USE_XO);
+
+ if (pdev->dev.parent->platform_data) {
+ pm80x_pdata = pdev->dev.parent->platform_data;
+ pdata = pm80x_pdata->rtc;
+ if (pdata)
+ info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+out_rtc:
+ pm80x_free_irq(chip, info->irq, info);
+out:
+ return ret;
+}
+
+static int __devexit pm80x_rtc_remove(struct platform_device *pdev)
+{
+ struct pm80x_rtc_info *info = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(info->rtc_dev);
+ pm80x_free_irq(info->chip, info->irq, info);
+ return 0;
+}
+
+static struct platform_driver pm80x_rtc_driver = {
+ .driver = {
+ .name = "88pm80x-rtc",
+ .owner = THIS_MODULE,
+ .pm = &pm80x_rtc_pm_ops,
+ },
+ .probe = pm80x_rtc_probe,
+ .remove = __devexit_p(pm80x_rtc_remove),
+};
+
+module_platform_driver(pm80x_rtc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x RTC driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-rtc");
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 370889d0489b..bf3c2f669c3c 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -89,22 +89,17 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (retval < 0)
return retval;
- /* Early AB8500 chips will not clear the rtc read request bit */
- if (abx500_get_chip_id(dev) == 0) {
- usleep_range(1000, 1000);
- } else {
- /* Wait for some cycles after enabling the rtc read in ab8500 */
- while (time_before(jiffies, timeout)) {
- retval = abx500_get_register_interruptible(dev,
- AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
- if (retval < 0)
- return retval;
-
- if (!(value & RTC_READ_REQUEST))
- break;
-
- usleep_range(1000, 5000);
- }
+ /* Wait for some cycles after enabling the rtc read in ab8500 */
+ while (time_before(jiffies, timeout)) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
+ if (retval < 0)
+ return retval;
+
+ if (!(value & RTC_READ_REQUEST))
+ break;
+
+ usleep_range(1000, 5000);
}
/* Read the Watchtime registers */
@@ -225,7 +220,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
- unsigned long mins, secs = 0;
+ unsigned long mins, secs = 0, cursec = 0;
+ struct rtc_time curtm;
if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
dev_dbg(dev, "year should be equal to or greater than %d\n",
@@ -237,6 +233,18 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
rtc_tm_to_time(&alarm->time, &secs);
/*
+ * Check whether alarm is set less than 1min.
+ * Since our RTC doesn't support alarm resolution less than 1min,
+ * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON
+ */
+ ab8500_rtc_read_time(dev, &curtm); /* Read current time */
+ rtc_tm_to_time(&curtm, &cursec);
+ if ((secs - cursec) < 59) {
+ dev_dbg(dev, "Alarm less than 1 minute not supported\r\n");
+ return -EINVAL;
+ }
+
+ /*
* Convert it to the number of seconds since 01-01-2000 00:00:00, since
* we only have a small counter in the RTC.
*/
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index a5b8a0c4ea84..76b2156d3c62 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -155,13 +155,10 @@ static int __exit coh901331_remove(struct platform_device *pdev)
struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
if (rtap) {
- free_irq(rtap->irq, rtap);
rtc_device_unregister(rtap->rtc);
+ clk_unprepare(rtap->clk);
clk_put(rtap->clk);
- iounmap(rtap->virtbase);
- release_mem_region(rtap->phybase, rtap->physize);
platform_set_drvdata(pdev, NULL);
- kfree(rtap);
}
return 0;
@@ -174,49 +171,43 @@ static int __init coh901331_probe(struct platform_device *pdev)
struct coh901331_port *rtap;
struct resource *res;
- rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL);
+ rtap = devm_kzalloc(&pdev->dev,
+ sizeof(struct coh901331_port), GFP_KERNEL);
if (!rtap)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto out_no_resource;
- }
+ if (!res)
+ return -ENOENT;
+
rtap->phybase = res->start;
rtap->physize = resource_size(res);
- if (request_mem_region(rtap->phybase, rtap->physize,
- "rtc-coh901331") == NULL) {
- ret = -EBUSY;
- goto out_no_memregion;
- }
+ if (devm_request_mem_region(&pdev->dev, rtap->phybase, rtap->physize,
+ "rtc-coh901331") == NULL)
+ return -EBUSY;
- rtap->virtbase = ioremap(rtap->phybase, rtap->physize);
- if (!rtap->virtbase) {
- ret = -ENOMEM;
- goto out_no_remap;
- }
+ rtap->virtbase = devm_ioremap(&pdev->dev, rtap->phybase, rtap->physize);
+ if (!rtap->virtbase)
+ return -ENOMEM;
rtap->irq = platform_get_irq(pdev, 0);
- if (request_irq(rtap->irq, coh901331_interrupt, 0,
- "RTC COH 901 331 Alarm", rtap)) {
- ret = -EIO;
- goto out_no_irq;
- }
+ if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0,
+ "RTC COH 901 331 Alarm", rtap))
+ return -EIO;
rtap->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(rtap->clk)) {
ret = PTR_ERR(rtap->clk);
dev_err(&pdev->dev, "could not get clock\n");
- goto out_no_clk;
+ return ret;
}
/* We enable/disable the clock only to assure it works */
- ret = clk_enable(rtap->clk);
+ ret = clk_prepare_enable(rtap->clk);
if (ret) {
dev_err(&pdev->dev, "could not enable clock\n");
- goto out_no_clk_enable;
+ goto out_no_clk_prepenable;
}
clk_disable(rtap->clk);
@@ -232,18 +223,9 @@ static int __init coh901331_probe(struct platform_device *pdev)
out_no_rtc:
platform_set_drvdata(pdev, NULL);
- out_no_clk_enable:
+ clk_unprepare(rtap->clk);
+ out_no_clk_prepenable:
clk_put(rtap->clk);
- out_no_clk:
- free_irq(rtap->irq, rtap);
- out_no_irq:
- iounmap(rtap->virtbase);
- out_no_remap:
- platform_set_drvdata(pdev, NULL);
- out_no_memregion:
- release_mem_region(rtap->phybase, SZ_4K);
- out_no_resource:
- kfree(rtap);
return ret;
}
@@ -265,6 +247,7 @@ static int coh901331_suspend(struct platform_device *pdev, pm_message_t state)
writel(0, rtap->virtbase + COH901331_IRQ_MASK);
clk_disable(rtap->clk);
}
+ clk_unprepare(rtap->clk);
return 0;
}
@@ -272,6 +255,7 @@ static int coh901331_resume(struct platform_device *pdev)
{
struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
+ clk_prepare(rtap->clk);
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(rtap->irq);
} else {
@@ -293,6 +277,7 @@ static void coh901331_shutdown(struct platform_device *pdev)
clk_enable(rtap->clk);
writel(0, rtap->virtbase + COH901331_IRQ_MASK);
clk_disable(rtap->clk);
+ clk_unprepare(rtap->clk);
}
static struct platform_driver coh901331_driver = {
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
index da6ab5291a41..78070255bd3f 100644
--- a/drivers/rtc/rtc-da9052.c
+++ b/drivers/rtc/rtc-da9052.c
@@ -245,7 +245,7 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev)
"ALM", rtc);
if (ret != 0) {
rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
- goto err_mem;
+ return ret;
}
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
@@ -259,8 +259,6 @@ static int __devinit da9052_rtc_probe(struct platform_device *pdev)
err_free_irq:
free_irq(rtc->irq, rtc);
-err_mem:
- devm_kfree(&pdev->dev, rtc);
return ret;
}
@@ -271,7 +269,6 @@ static int __devexit da9052_rtc_remove(struct platform_device *pdev)
rtc_device_unregister(rtc->rtc);
free_irq(rtc->irq, rtc);
platform_set_drvdata(pdev, NULL);
- devm_kfree(&pdev->dev, rtc);
return 0;
}
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
index 1459055a83aa..34e4349611db 100644
--- a/drivers/rtc/rtc-max8925.c
+++ b/drivers/rtc/rtc-max8925.c
@@ -69,6 +69,7 @@ struct max8925_rtc_info {
struct max8925_chip *chip;
struct i2c_client *rtc;
struct device *dev;
+ int irq;
};
static irqreturn_t rtc_update_handler(int irq, void *data)
@@ -250,7 +251,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_rtc_info *info;
- int irq, ret;
+ int ret;
info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
if (!info)
@@ -258,13 +259,13 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
info->chip = chip;
info->rtc = chip->rtc;
info->dev = &pdev->dev;
- irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
+ info->irq = platform_get_irq(pdev, 0);
- ret = request_threaded_irq(irq, NULL, rtc_update_handler,
+ ret = request_threaded_irq(info->irq, NULL, rtc_update_handler,
IRQF_ONESHOT, "rtc-alarm0", info);
if (ret < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
- irq, ret);
+ info->irq, ret);
goto out_irq;
}
@@ -285,7 +286,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
return 0;
out_rtc:
platform_set_drvdata(pdev, NULL);
- free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+ free_irq(info->irq, info);
out_irq:
kfree(info);
return ret;
@@ -296,7 +297,7 @@ static int __devexit max8925_rtc_remove(struct platform_device *pdev)
struct max8925_rtc_info *info = platform_get_drvdata(pdev);
if (info) {
- free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+ free_irq(info->irq, info);
rtc_device_unregister(info->rtc_dev);
kfree(info);
}
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index 546f6850bffb..2643d8874925 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -404,9 +404,12 @@ static const struct platform_device_id mc13xxx_rtc_idtable[] = {
.name = "mc13783-rtc",
}, {
.name = "mc13892-rtc",
+ }, {
+ .name = "mc34708-rtc",
},
- { }
+ { /* sentinel */ }
};
+MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
static struct platform_driver mc13xxx_rtc_driver = {
.id_table = mc13xxx_rtc_idtable,
@@ -432,4 +435,3 @@ module_exit(mc13xxx_rtc_exit);
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 97a3284bb7c6..c2fe426a6ef2 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -19,6 +19,7 @@
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/of.h>
#define DRV_VERSION "0.4.3"
@@ -285,9 +286,19 @@ static const struct i2c_device_id pcf8563_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcf8563_id);
+#ifdef CONFIG_OF
+static const struct of_device_id pcf8563_of_match[] __devinitconst = {
+ { .compatible = "nxp,pcf8563" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf8563_of_match);
+#endif
+
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf8563_of_match),
},
.probe = pcf8563_probe,
.remove = pcf8563_remove,
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index cc0533994f6e..08378e3cc21c 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -68,11 +68,26 @@
#define RTC_TIMER_FREQ 32768
+/**
+ * struct pl031_vendor_data - per-vendor variations
+ * @ops: the vendor-specific operations used on this silicon version
+ * @clockwatch: if this is an ST Microelectronics silicon version with a
+ * clockwatch function
+ * @st_weekday: if this is an ST Microelectronics silicon version that need
+ * the weekday fix
+ * @irqflags: special IRQ flags per variant
+ */
+struct pl031_vendor_data {
+ struct rtc_class_ops ops;
+ bool clockwatch;
+ bool st_weekday;
+ unsigned long irqflags;
+};
+
struct pl031_local {
+ struct pl031_vendor_data *vendor;
struct rtc_device *rtc;
void __iomem *base;
- u8 hw_designer;
- u8 hw_revision:4;
};
static int pl031_alarm_irq_enable(struct device *dev,
@@ -303,7 +318,8 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
struct pl031_local *ldata;
- struct rtc_class_ops *ops = id->data;
+ struct pl031_vendor_data *vendor = id->data;
+ struct rtc_class_ops *ops = &vendor->ops;
unsigned long time;
ret = amba_request_regions(adev, NULL);
@@ -315,6 +331,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
ret = -ENOMEM;
goto out;
}
+ ldata->vendor = vendor;
ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
@@ -325,14 +342,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
amba_set_drvdata(adev, ldata);
- ldata->hw_designer = amba_manf(adev);
- ldata->hw_revision = amba_rev(adev);
-
- dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer);
- dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision);
+ dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
+ dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
/* Enable the clockwatch on ST Variants */
- if (ldata->hw_designer == AMBA_VENDOR_ST)
+ if (vendor->clockwatch)
writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
ldata->base + RTC_CR);
@@ -340,7 +354,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
* On ST PL031 variants, the RTC reset value does not provide correct
* weekday for 2000-01-01. Correct the erroneous sunday to saturday.
*/
- if (ldata->hw_designer == AMBA_VENDOR_ST) {
+ if (vendor->st_weekday) {
if (readl(ldata->base + RTC_YDR) == 0x2000) {
time = readl(ldata->base + RTC_DR);
if ((time &
@@ -361,7 +375,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
}
if (request_irq(adev->irq[0], pl031_interrupt,
- 0, "rtc-pl031", ldata)) {
+ vendor->irqflags, "rtc-pl031", ldata)) {
ret = -EIO;
goto out_no_irq;
}
@@ -383,48 +397,65 @@ err_req:
}
/* Operations for the original ARM version */
-static struct rtc_class_ops arm_pl031_ops = {
- .read_time = pl031_read_time,
- .set_time = pl031_set_time,
- .read_alarm = pl031_read_alarm,
- .set_alarm = pl031_set_alarm,
- .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data arm_pl031 = {
+ .ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .irqflags = IRQF_NO_SUSPEND,
};
/* The First ST derivative */
-static struct rtc_class_ops stv1_pl031_ops = {
- .read_time = pl031_read_time,
- .set_time = pl031_set_time,
- .read_alarm = pl031_read_alarm,
- .set_alarm = pl031_set_alarm,
- .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data stv1_pl031 = {
+ .ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .clockwatch = true,
+ .st_weekday = true,
+ .irqflags = IRQF_NO_SUSPEND,
};
/* And the second ST derivative */
-static struct rtc_class_ops stv2_pl031_ops = {
- .read_time = pl031_stv2_read_time,
- .set_time = pl031_stv2_set_time,
- .read_alarm = pl031_stv2_read_alarm,
- .set_alarm = pl031_stv2_set_alarm,
- .alarm_irq_enable = pl031_alarm_irq_enable,
+static struct pl031_vendor_data stv2_pl031 = {
+ .ops = {
+ .read_time = pl031_stv2_read_time,
+ .set_time = pl031_stv2_set_time,
+ .read_alarm = pl031_stv2_read_alarm,
+ .set_alarm = pl031_stv2_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .clockwatch = true,
+ .st_weekday = true,
+ /*
+ * This variant shares the IRQ with another block and must not
+ * suspend that IRQ line.
+ */
+ .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND,
};
static struct amba_id pl031_ids[] = {
{
.id = 0x00041031,
.mask = 0x000fffff,
- .data = &arm_pl031_ops,
+ .data = &arm_pl031,
},
/* ST Micro variants */
{
.id = 0x00180031,
.mask = 0x00ffffff,
- .data = &stv1_pl031_ops,
+ .data = &stv1_pl031,
},
{
.id = 0x00280031,
.mask = 0x00ffffff,
- .data = &stv2_pl031_ops,
+ .data = &stv2_pl031,
},
{0, 0},
};
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
index 33b6ba0afa0d..2c183ebff715 100644
--- a/drivers/rtc/rtc-r9701.c
+++ b/drivers/rtc/rtc-r9701.c
@@ -138,8 +138,7 @@ static int __devinit r9701_probe(struct spi_device *spi)
* contain invalid values. If so, try to write a default date:
* 2000/1/1 00:00:00
*/
- r9701_get_datetime(&spi->dev, &dt);
- if (rtc_valid_tm(&dt)) {
+ if (r9701_get_datetime(&spi->dev, &dt)) {
dev_info(&spi->dev, "trying to repair invalid date/time\n");
dt.tm_sec = 0;
dt.tm_min = 0;
@@ -148,7 +147,8 @@ static int __devinit r9701_probe(struct spi_device *spi)
dt.tm_mon = 0;
dt.tm_year = 100;
- if (r9701_set_datetime(&spi->dev, &dt)) {
+ if (r9701_set_datetime(&spi->dev, &dt) ||
+ r9701_get_datetime(&spi->dev, &dt)) {
dev_err(&spi->dev, "cannot repair RTC register\n");
return -ENODEV;
}
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 7e6af0b22f17..bfbd92c8d1c9 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -26,10 +26,10 @@
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
#include <mach/hardware.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <plat/regs-rtc.h>
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 59c6245e0421..ea5c6f857ca5 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -24,7 +24,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-
+#include <linux/random.h>
/*
* R16416 (0x4020) - RTC Write Counter
@@ -96,6 +96,26 @@ struct wm831x_rtc {
unsigned int alarm_enabled:1;
};
+static void wm831x_rtc_add_randomness(struct wm831x *wm831x)
+{
+ int ret;
+ u16 reg;
+
+ /*
+ * The write counter contains a pseudo-random number which is
+ * regenerated every time we set the RTC so it should be a
+ * useful per-system source of entropy.
+ */
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
+ if (ret >= 0) {
+ reg = ret;
+ add_device_randomness(&reg, sizeof(reg));
+ } else {
+ dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
+ ret);
+ }
+}
+
/*
* Read current time and date in RTC
*/
@@ -431,6 +451,8 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
alm_irq, ret);
}
+ wm831x_rtc_add_randomness(wm831x);
+
return 0;
err:
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 2d1e68db9b3f..e894ca7b54c0 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -4146,45 +4146,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
static void
fc_bsg_remove(struct request_queue *q)
{
- struct request *req; /* block request */
- int counts; /* totals for request_list count and starved */
-
if (q) {
- /* Stop taking in new requests */
- spin_lock_irq(q->queue_lock);
- blk_stop_queue(q);
-
- /* drain all requests in the queue */
- while (1) {
- /* need the lock to fetch a request
- * this may fetch the same reqeust as the previous pass
- */
- req = blk_fetch_request(q);
- /* save requests in use and starved */
- counts = q->rq.count[0] + q->rq.count[1] +
- q->rq.starved[0] + q->rq.starved[1];
- spin_unlock_irq(q->queue_lock);
- /* any requests still outstanding? */
- if (counts == 0)
- break;
-
- /* This may be the same req as the previous iteration,
- * always send the blk_end_request_all after a prefetch.
- * It is not okay to not end the request because the
- * prefetch started the request.
- */
- if (req) {
- /* return -ENXIO to indicate that this queue is
- * going away
- */
- req->errors = -ENXIO;
- blk_end_request_all(req, -ENXIO);
- }
-
- msleep(200); /* allow bsg to possibly finish */
- spin_lock_irq(q->queue_lock);
- }
-
bsg_unregister_queue(q);
blk_cleanup_queue(q);
}
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 09809d06eccb..fa1dfaa83e32 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc,
struct iscsi_cls_host *ihost = shost->shost_data;
if (ihost->bsg_q) {
- bsg_remove_queue(ihost->bsg_q);
+ bsg_unregister_queue(ihost->bsg_q);
blk_cleanup_queue(ihost->bsg_q);
}
return 0;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4cde4fb0cd6c..5f84b5563c2d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -144,6 +144,15 @@ config SPI_EP93XX
This enables using the Cirrus EP93xx SPI controller in master
mode.
+config SPI_FALCON
+ tristate "Falcon SPI controller support"
+ depends on SOC_FALCON
+ help
+ The external bus unit (EBU) found on the FALC-ON SoC has SPI
+ emulation that is designed for serial flash access. This driver
+ has only been tested with m25p80 type chips. The hardware has no
+ support for other types of SPI peripherals.
+
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
depends on GENERIC_GPIO
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 273f50d1127a..3920dcf4c740 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
+obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
new file mode 100644
index 000000000000..8f6aa735a24c
--- /dev/null
+++ b/drivers/spi/spi-falcon.c
@@ -0,0 +1,469 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <lantiq_soc.h>
+
+#define DRV_NAME "sflash-falcon"
+
+#define FALCON_SPI_XFER_BEGIN (1 << 0)
+#define FALCON_SPI_XFER_END (1 << 1)
+
+/* Bus Read Configuration Register0 */
+#define BUSRCON0 0x00000010
+/* Bus Write Configuration Register0 */
+#define BUSWCON0 0x00000018
+/* Serial Flash Configuration Register */
+#define SFCON 0x00000080
+/* Serial Flash Time Register */
+#define SFTIME 0x00000084
+/* Serial Flash Status Register */
+#define SFSTAT 0x00000088
+/* Serial Flash Command Register */
+#define SFCMD 0x0000008C
+/* Serial Flash Address Register */
+#define SFADDR 0x00000090
+/* Serial Flash Data Register */
+#define SFDATA 0x00000094
+/* Serial Flash I/O Control Register */
+#define SFIO 0x00000098
+/* EBU Clock Control Register */
+#define EBUCC 0x000000C4
+
+/* Dummy Phase Length */
+#define SFCMD_DUMLEN_OFFSET 16
+#define SFCMD_DUMLEN_MASK 0x000F0000
+/* Chip Select */
+#define SFCMD_CS_OFFSET 24
+#define SFCMD_CS_MASK 0x07000000
+/* field offset */
+#define SFCMD_ALEN_OFFSET 20
+#define SFCMD_ALEN_MASK 0x00700000
+/* SCK Rise-edge Position */
+#define SFTIME_SCKR_POS_OFFSET 8
+#define SFTIME_SCKR_POS_MASK 0x00000F00
+/* SCK Period */
+#define SFTIME_SCK_PER_OFFSET 0
+#define SFTIME_SCK_PER_MASK 0x0000000F
+/* SCK Fall-edge Position */
+#define SFTIME_SCKF_POS_OFFSET 12
+#define SFTIME_SCKF_POS_MASK 0x0000F000
+/* Device Size */
+#define SFCON_DEV_SIZE_A23_0 0x03000000
+#define SFCON_DEV_SIZE_MASK 0x0F000000
+/* Read Data Position */
+#define SFTIME_RD_POS_MASK 0x000F0000
+/* Data Output */
+#define SFIO_UNUSED_WD_MASK 0x0000000F
+/* Command Opcode mask */
+#define SFCMD_OPC_MASK 0x000000FF
+/* dlen bytes of data to write */
+#define SFCMD_DIR_WRITE 0x00000100
+/* Data Length offset */
+#define SFCMD_DLEN_OFFSET 9
+/* Command Error */
+#define SFSTAT_CMD_ERR 0x20000000
+/* Access Command Pending */
+#define SFSTAT_CMD_PEND 0x00400000
+/* Frequency set to 100MHz. */
+#define EBUCC_EBUDIV_SELF100 0x00000001
+/* Serial Flash */
+#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000
+/* 8-bit multiplexed */
+#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000
+/* Serial Flash */
+#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000
+/* Chip Select after opcode */
+#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000
+
+#define CLOCK_100M 100000000
+#define CLOCK_50M 50000000
+
+struct falcon_sflash {
+ u32 sfcmd; /* for caching of opcode, direction, ... */
+ struct spi_master *master;
+};
+
+int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
+ unsigned long flags)
+{
+ struct device *dev = &spi->dev;
+ struct falcon_sflash *priv = spi_master_get_devdata(spi->master);
+ const u8 *txp = t->tx_buf;
+ u8 *rxp = t->rx_buf;
+ unsigned int bytelen = ((8 * t->len + 7) / 8);
+ unsigned int len, alen, dumlen;
+ u32 val;
+ enum {
+ state_init,
+ state_command_prepare,
+ state_write,
+ state_read,
+ state_disable_cs,
+ state_end
+ } state = state_init;
+
+ do {
+ switch (state) {
+ case state_init: /* detect phase of upper layer sequence */
+ {
+ /* initial write ? */
+ if (flags & FALCON_SPI_XFER_BEGIN) {
+ if (!txp) {
+ dev_err(dev,
+ "BEGIN without tx data!\n");
+ return -ENODATA;
+ }
+ /*
+ * Prepare the parts of the sfcmd register,
+ * which should not change during a sequence!
+ * Only exception are the length fields,
+ * especially alen and dumlen.
+ */
+
+ priv->sfcmd = ((spi->chip_select
+ << SFCMD_CS_OFFSET)
+ & SFCMD_CS_MASK);
+ priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED;
+ priv->sfcmd |= *txp;
+ txp++;
+ bytelen--;
+ if (bytelen) {
+ /*
+ * more data:
+ * maybe address and/or dummy
+ */
+ state = state_command_prepare;
+ break;
+ } else {
+ dev_dbg(dev, "write cmd %02X\n",
+ priv->sfcmd & SFCMD_OPC_MASK);
+ }
+ }
+ /* continued write ? */
+ if (txp && bytelen) {
+ state = state_write;
+ break;
+ }
+ /* read data? */
+ if (rxp && bytelen) {
+ state = state_read;
+ break;
+ }
+ /* end of sequence? */
+ if (flags & FALCON_SPI_XFER_END)
+ state = state_disable_cs;
+ else
+ state = state_end;
+ break;
+ }
+ /* collect tx data for address and dummy phase */
+ case state_command_prepare:
+ {
+ /* txp is valid, already checked */
+ val = 0;
+ alen = 0;
+ dumlen = 0;
+ while (bytelen > 0) {
+ if (alen < 3) {
+ val = (val << 8) | (*txp++);
+ alen++;
+ } else if ((dumlen < 15) && (*txp == 0)) {
+ /*
+ * assume dummy bytes are set to 0
+ * from upper layer
+ */
+ dumlen++;
+ txp++;
+ } else {
+ break;
+ }
+ bytelen--;
+ }
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK);
+ priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) |
+ (dumlen << SFCMD_DUMLEN_OFFSET);
+ if (alen > 0)
+ ltq_ebu_w32(val, SFADDR);
+
+ dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n",
+ priv->sfcmd & SFCMD_OPC_MASK,
+ alen, val, dumlen);
+
+ if (bytelen > 0) {
+ /* continue with write */
+ state = state_write;
+ } else if (flags & FALCON_SPI_XFER_END) {
+ /* end of sequence? */
+ state = state_disable_cs;
+ } else {
+ /*
+ * go to end and expect another
+ * call (read or write)
+ */
+ state = state_end;
+ }
+ break;
+ }
+ case state_write:
+ {
+ /* txp still valid */
+ priv->sfcmd |= SFCMD_DIR_WRITE;
+ len = 0;
+ val = 0;
+ do {
+ if (bytelen--)
+ val |= (*txp++) << (8 * len++);
+ if ((flags & FALCON_SPI_XFER_END)
+ && (bytelen == 0)) {
+ priv->sfcmd &=
+ ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ }
+ if ((len == 4) || (bytelen == 0)) {
+ ltq_ebu_w32(val, SFDATA);
+ ltq_ebu_w32(priv->sfcmd
+ | (len<<SFCMD_DLEN_OFFSET),
+ SFCMD);
+ len = 0;
+ val = 0;
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK
+ | SFCMD_DUMLEN_MASK);
+ }
+ } while (bytelen);
+ state = state_end;
+ break;
+ }
+ case state_read:
+ {
+ /* read data */
+ priv->sfcmd &= ~SFCMD_DIR_WRITE;
+ do {
+ if ((flags & FALCON_SPI_XFER_END)
+ && (bytelen <= 4)) {
+ priv->sfcmd &=
+ ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ }
+ len = (bytelen > 4) ? 4 : bytelen;
+ bytelen -= len;
+ ltq_ebu_w32(priv->sfcmd
+ | (len << SFCMD_DLEN_OFFSET), SFCMD);
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK
+ | SFCMD_DUMLEN_MASK);
+ do {
+ val = ltq_ebu_r32(SFSTAT);
+ if (val & SFSTAT_CMD_ERR) {
+ /* reset error status */
+ dev_err(dev, "SFSTAT: CMD_ERR");
+ dev_err(dev, " (%x)\n", val);
+ ltq_ebu_w32(SFSTAT_CMD_ERR,
+ SFSTAT);
+ return -EBADE;
+ }
+ } while (val & SFSTAT_CMD_PEND);
+ val = ltq_ebu_r32(SFDATA);
+ do {
+ *rxp = (val & 0xFF);
+ rxp++;
+ val >>= 8;
+ len--;
+ } while (len);
+ } while (bytelen);
+ state = state_end;
+ break;
+ }
+ case state_disable_cs:
+ {
+ priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET),
+ SFCMD);
+ val = ltq_ebu_r32(SFSTAT);
+ if (val & SFSTAT_CMD_ERR) {
+ /* reset error status */
+ dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val);
+ ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT);
+ return -EBADE;
+ }
+ state = state_end;
+ break;
+ }
+ case state_end:
+ break;
+ }
+ } while (state != state_end);
+
+ return 0;
+}
+
+static int falcon_sflash_setup(struct spi_device *spi)
+{
+ unsigned int i;
+ unsigned long flags;
+
+ if (spi->chip_select > 0)
+ return -ENODEV;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+
+ if (spi->max_speed_hz >= CLOCK_100M) {
+ /* set EBU clock to 100 MHz */
+ ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC);
+ i = 1; /* divider */
+ } else {
+ /* set EBU clock to 50 MHz */
+ ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC);
+
+ /* search for suitable divider */
+ for (i = 1; i < 7; i++) {
+ if (CLOCK_50M / i <= spi->max_speed_hz)
+ break;
+ }
+ }
+
+ /* setup period of serial clock */
+ ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK
+ | SFTIME_SCKR_POS_MASK
+ | SFTIME_SCK_PER_MASK,
+ (i << SFTIME_SCKR_POS_OFFSET)
+ | (i << (SFTIME_SCK_PER_OFFSET + 1)),
+ SFTIME);
+
+ /*
+ * set some bits of unused_wd, to not trigger HOLD/WP
+ * signals on non QUAD flashes
+ */
+ ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO);
+
+ ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX,
+ BUSRCON0);
+ ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0);
+ /* set address wrap around to maximum for 24-bit addresses */
+ ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON);
+
+ spin_unlock_irqrestore(&ebu_lock, flags);
+
+ return 0;
+}
+
+static int falcon_sflash_prepare_xfer(struct spi_master *master)
+{
+ return 0;
+}
+
+static int falcon_sflash_unprepare_xfer(struct spi_master *master)
+{
+ return 0;
+}
+
+static int falcon_sflash_xfer_one(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct falcon_sflash *priv = spi_master_get_devdata(master);
+ struct spi_transfer *t;
+ unsigned long spi_flags;
+ unsigned long flags;
+ int ret = 0;
+
+ priv->sfcmd = 0;
+ m->actual_length = 0;
+
+ spi_flags = FALCON_SPI_XFER_BEGIN;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (list_is_last(&t->transfer_list, &m->transfers))
+ spi_flags |= FALCON_SPI_XFER_END;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+ ret = falcon_sflash_xfer(m->spi, t, spi_flags);
+ spin_unlock_irqrestore(&ebu_lock, flags);
+
+ if (ret)
+ break;
+
+ m->actual_length += t->len;
+
+ WARN_ON(t->delay_usecs || t->cs_change);
+ spi_flags = 0;
+ }
+
+ m->status = ret;
+ m->complete(m->context);
+
+ return 0;
+}
+
+static int __devinit falcon_sflash_probe(struct platform_device *pdev)
+{
+ struct falcon_sflash *priv;
+ struct spi_master *master;
+ int ret;
+
+ if (ltq_boot_select() != BS_SPI) {
+ dev_err(&pdev->dev, "invalid bootstrap options\n");
+ return -ENODEV;
+ }
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*priv));
+ if (!master)
+ return -ENOMEM;
+
+ priv = spi_master_get_devdata(master);
+ priv->master = master;
+
+ master->mode_bits = SPI_MODE_3;
+ master->num_chipselect = 1;
+ master->bus_num = -1;
+ master->setup = falcon_sflash_setup;
+ master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
+ master->transfer_one_message = falcon_sflash_xfer_one;
+ master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
+ master->dev.of_node = pdev->dev.of_node;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = spi_register_master(master);
+ if (ret)
+ spi_master_put(master);
+ return ret;
+}
+
+static int __devexit falcon_sflash_remove(struct platform_device *pdev)
+{
+ struct falcon_sflash *priv = platform_get_drvdata(pdev);
+
+ spi_unregister_master(priv->master);
+
+ return 0;
+}
+
+static const struct of_device_id falcon_sflash_match[] = {
+ { .compatible = "lantiq,sflash-falcon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, falcon_sflash_match);
+
+static struct platform_driver falcon_sflash_driver = {
+ .probe = falcon_sflash_probe,
+ .remove = __devexit_p(falcon_sflash_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = falcon_sflash_match,
+ }
+};
+
+module_platform_driver(falcon_sflash_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver");
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 7d46b15e1520..bc4778175e34 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -28,6 +28,8 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -39,7 +41,6 @@
#include <linux/spi/spi.h>
-#include <plat/dma.h>
#include <plat/clock.h>
#include <plat/mcspi.h>
@@ -93,8 +94,8 @@
/* We have 2 DMA channels per CS, one for RX and one for TX */
struct omap2_mcspi_dma {
- int dma_tx_channel;
- int dma_rx_channel;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
int dma_tx_sync_dev;
int dma_rx_sync_dev;
@@ -300,20 +301,46 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
return 0;
}
+static void omap2_mcspi_rx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_rx_completion);
+
+ /* We must disable the DMA RX request */
+ omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_tx_completion);
+
+ /* We must disable the DMA TX request */
+ omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
static unsigned
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
{
struct omap2_mcspi *mcspi;
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi_dma *mcspi_dma;
- unsigned int count, c;
- unsigned long base, tx_reg, rx_reg;
- int word_len, data_type, element_count;
+ unsigned int count;
+ int word_len, element_count;
int elements = 0;
u32 l;
u8 * rx;
const u8 * tx;
void __iomem *chstat_reg;
+ struct dma_slave_config cfg;
+ enum dma_slave_buswidth width;
+ unsigned es;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -321,68 +348,92 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+ if (cs->word_len <= 8) {
+ width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ es = 1;
+ } else if (cs->word_len <= 16) {
+ width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ es = 2;
+ } else {
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ es = 4;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+ cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+ cfg.src_addr_width = width;
+ cfg.dst_addr_width = width;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
+
+ if (xfer->tx_buf && mcspi_dma->dma_tx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+
+ dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->tx_dma;
+ sg_dma_len(&sg) = xfer->len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_tx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
+ if (xfer->rx_buf && mcspi_dma->dma_rx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+ size_t len = xfer->len - es;
+
+ dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+ if (l & OMAP2_MCSPI_CHCONF_TURBO)
+ len -= es;
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->rx_dma;
+ sg_dma_len(&sg) = len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_rx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
count = xfer->len;
- c = count;
word_len = cs->word_len;
- base = cs->phys;
- tx_reg = base + OMAP2_MCSPI_TX0;
- rx_reg = base + OMAP2_MCSPI_RX0;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
if (word_len <= 8) {
- data_type = OMAP_DMA_DATA_TYPE_S8;
element_count = count;
} else if (word_len <= 16) {
- data_type = OMAP_DMA_DATA_TYPE_S16;
element_count = count >> 1;
} else /* word_len <= 32 */ {
- data_type = OMAP_DMA_DATA_TYPE_S32;
element_count = count >> 2;
}
if (tx != NULL) {
- omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
- data_type, element_count, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_tx_sync_dev, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- tx_reg, 0, 0);
-
- omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->tx_dma, 0, 0);
- }
-
- if (rx != NULL) {
- elements = element_count - 1;
- if (l & OMAP2_MCSPI_CHCONF_TURBO)
- elements--;
-
- omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
- data_type, elements, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_rx_sync_dev, 1);
-
- omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- rx_reg, 0, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->rx_dma, 0, 0);
- }
-
- if (tx != NULL) {
- omap_start_dma(mcspi_dma->dma_tx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
}
if (rx != NULL) {
- omap_start_dma(mcspi_dma->dma_rx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_rx);
omap2_mcspi_set_dma_req(spi, 1, 1);
}
@@ -408,7 +459,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
DMA_FROM_DEVICE);
omap2_mcspi_set_enable(spi, 0);
+ elements = element_count - 1;
+
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+ elements--;
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
& OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -725,64 +779,38 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
return 0;
}
-static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_rx_completion);
-
- /* We must disable the DMA RX request */
- omap2_mcspi_set_dma_req(spi, 1, 0);
-}
-
-static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_tx_completion);
-
- /* We must disable the DMA TX request */
- omap2_mcspi_set_dma_req(spi, 0, 0);
-}
-
static int omap2_mcspi_request_dma(struct spi_device *spi)
{
struct spi_master *master = spi->master;
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
+ dma_cap_mask_t mask;
+ unsigned sig;
mcspi = spi_master_get_devdata(master);
mcspi_dma = mcspi->dma_channels + spi->chip_select;
- if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
- omap2_mcspi_dma_rx_callback, spi,
- &mcspi_dma->dma_rx_channel)) {
- dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
+ init_completion(&mcspi_dma->dma_rx_completion);
+ init_completion(&mcspi_dma->dma_tx_completion);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ sig = mcspi_dma->dma_rx_sync_dev;
+ mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_rx) {
+ dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
return -EAGAIN;
}
- if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
- omap2_mcspi_dma_tx_callback, spi,
- &mcspi_dma->dma_tx_channel)) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
- dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
+ sig = mcspi_dma->dma_tx_sync_dev;
+ mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_tx) {
+ dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
return -EAGAIN;
}
- init_completion(&mcspi_dma->dma_rx_completion);
- init_completion(&mcspi_dma->dma_tx_completion);
-
return 0;
}
@@ -814,8 +842,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
list_add_tail(&cs->node, &ctx->cs);
}
- if (mcspi_dma->dma_rx_channel == -1
- || mcspi_dma->dma_tx_channel == -1) {
+ if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
ret = omap2_mcspi_request_dma(spi);
if (ret < 0)
return ret;
@@ -850,13 +877,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
if (spi->chip_select < spi->master->num_chipselect) {
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
- if (mcspi_dma->dma_rx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
+ if (mcspi_dma->dma_rx) {
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
}
- if (mcspi_dma->dma_tx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_tx_channel);
- mcspi_dma->dma_tx_channel = -1;
+ if (mcspi_dma->dma_tx) {
+ dma_release_channel(mcspi_dma->dma_tx);
+ mcspi_dma->dma_tx = NULL;
}
}
}
@@ -1176,7 +1203,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_rx_channel = -1;
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
sprintf(dma_ch_name, "tx%d", i);
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
@@ -1187,7 +1213,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_tx_channel = -1;
mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
}
diff --git a/drivers/staging/bcm/Misc.c b/drivers/staging/bcm/Misc.c
index 9a60d4cd2184..f545716c666d 100644
--- a/drivers/staging/bcm/Misc.c
+++ b/drivers/staging/bcm/Misc.c
@@ -157,12 +157,7 @@ static int create_worker_threads(struct bcm_mini_adapter *psAdapter)
static struct file *open_firmware_file(struct bcm_mini_adapter *Adapter, const char *path)
{
- struct file *flp = NULL;
- mm_segment_t oldfs;
- oldfs = get_fs();
- set_fs(get_ds());
- flp = filp_open(path, O_RDONLY, S_IRWXU);
- set_fs(oldfs);
+ struct file *flp = filp_open(path, O_RDONLY, S_IRWXU);
if (IS_ERR(flp)) {
pr_err(DRV_NAME "Unable To Open File %s, err %ld", path, PTR_ERR(flp));
flp = NULL;
@@ -183,14 +178,12 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
{
int errorno = 0;
struct file *flp = NULL;
- mm_segment_t oldfs;
struct timeval tv = {0};
flp = open_firmware_file(Adapter, path);
if (!flp) {
- errorno = -ENOENT;
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Unable to Open %s\n", path);
- goto exit_download;
+ return -ENOENT;
}
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Opened file is = %s and length =0x%lx to be downloaded at =0x%x", path, (unsigned long)flp->f_dentry->d_inode->i_size, loc);
do_gettimeofday(&tv);
@@ -201,10 +194,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
errorno = -EIO;
goto exit_download;
}
- oldfs = get_fs();
- set_fs(get_ds());
vfs_llseek(flp, 0, 0);
- set_fs(oldfs);
if (Adapter->bcm_file_readback_from_chip(Adapter->pvInterfaceAdapter, flp, loc)) {
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Failed to read back firmware!");
errorno = -EIO;
@@ -212,12 +202,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
}
exit_download:
- oldfs = get_fs();
- set_fs(get_ds());
- if (flp && !(IS_ERR(flp)))
- filp_close(flp, current->files);
- set_fs(oldfs);
-
+ filp_close(flp, NULL);
return errorno;
}
@@ -1056,10 +1041,8 @@ OUT:
static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
{
struct file *flp = NULL;
- mm_segment_t oldfs = {0};
char *buff;
int len = 0;
- loff_t pos = 0;
buff = kmalloc(BUFFER_1K, GFP_KERNEL);
if (!buff)
@@ -1079,20 +1062,16 @@ static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
Adapter->pstargetparams = NULL;
return -ENOENT;
}
- oldfs = get_fs();
- set_fs(get_ds());
- len = vfs_read(flp, (void __user __force *)buff, BUFFER_1K, &pos);
- set_fs(oldfs);
+ len = kernel_read(flp, 0, buff, BUFFER_1K);
+ filp_close(flp, NULL);
if (len != sizeof(STARGETPARAMS)) {
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Mismatch in Target Param Structure!\n");
kfree(buff);
kfree(Adapter->pstargetparams);
Adapter->pstargetparams = NULL;
- filp_close(flp, current->files);
return -ENOENT;
}
- filp_close(flp, current->files);
/* Check for autolink in config params */
/*
diff --git a/drivers/staging/gdm72xx/sdio_boot.c b/drivers/staging/gdm72xx/sdio_boot.c
index 760efee23d4a..65624bca8b3a 100644
--- a/drivers/staging/gdm72xx/sdio_boot.c
+++ b/drivers/staging/gdm72xx/sdio_boot.c
@@ -66,9 +66,8 @@ static int download_image(struct sdio_func *func, char *img_name)
return -ENOENT;
}
- if (filp->f_dentry)
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
printk(KERN_ERR "Invalid file type: %s\n", img_name);
ret = -EINVAL;
goto out;
@@ -123,7 +122,7 @@ static int download_image(struct sdio_func *func, char *img_name)
pno++;
}
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
return ret;
}
diff --git a/drivers/staging/gdm72xx/usb_boot.c b/drivers/staging/gdm72xx/usb_boot.c
index fef290c38db6..e3dbd5a552ca 100644
--- a/drivers/staging/gdm72xx/usb_boot.c
+++ b/drivers/staging/gdm72xx/usb_boot.c
@@ -173,14 +173,12 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "Can't find %s.\n", img_name);
- set_fs(fs);
ret = PTR_ERR(filp);
goto restore_fs;
}
- if (filp->f_dentry)
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
printk(KERN_ERR "Invalid file type: %s\n", img_name);
ret = -EINVAL;
goto out;
@@ -262,7 +260,7 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
ret = -EINVAL;
}
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
restore_fs:
set_fs(fs);
@@ -322,13 +320,11 @@ static int em_download_image(struct usb_device *usbdev, char *path,
goto restore_fs;
}
- if (filp->f_dentry) {
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
- printk(KERN_ERR "Invalid file type: %s\n", path);
- ret = -EINVAL;
- goto out;
- }
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
+ printk(KERN_ERR "Invalid file type: %s\n", path);
+ ret = -EINVAL;
+ goto out;
}
buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
@@ -364,7 +360,7 @@ static int em_download_image(struct usb_device *usbdev, char *path,
goto out;
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
restore_fs:
set_fs(fs);
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index c365cdf714ea..ebe5a27c06f5 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -971,20 +971,7 @@ static struct pci_driver pci_driver = {
.remove = __devexit_p(dt3155_remove),
};
-static int __init
-dt3155_init_module(void)
-{
- return pci_register_driver(&pci_driver);
-}
-
-static void __exit
-dt3155_exit_module(void)
-{
- pci_unregister_driver(&pci_driver);
-}
-
-module_init(dt3155_init_module);
-module_exit(dt3155_exit_module);
+module_pci_driver(pci_driver);
MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");
MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");
diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
index a1c45e4dcdce..8269c77dbf7d 100644
--- a/drivers/staging/media/easycap/easycap_main.c
+++ b/drivers/staging/media/easycap/easycap_main.c
@@ -3083,6 +3083,7 @@ static int create_video_urbs(struct easycap *peasycap)
peasycap->allocation_video_urb += 1;
pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
if (!pdata_urb) {
+ usb_free_urb(purb);
SAM("ERROR: Could not allocate struct data_urb.\n");
return -ENOMEM;
}
diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c
index 4d20e9f74118..951007a3fc96 100644
--- a/drivers/staging/media/lirc/lirc_bt829.c
+++ b/drivers/staging/media/lirc/lirc_bt829.c
@@ -171,7 +171,7 @@ static void cycle_delay(int cycle)
}
-static int poll_main()
+static int poll_main(void)
{
unsigned char status_high, status_low;
diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index 945d9623550b..4afc3b419738 100644
--- a/drivers/staging/media/lirc/lirc_sir.c
+++ b/drivers/staging/media/lirc/lirc_sir.c
@@ -52,6 +52,7 @@
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/fcntl.h>
+#include <linux/platform_device.h>
#ifdef LIRC_ON_SA1100
#include <asm/hardware.h>
#ifdef CONFIG_SA1100_COLLIE
@@ -487,9 +488,11 @@ static struct lirc_driver driver = {
.owner = THIS_MODULE,
};
+static struct platform_device *lirc_sir_dev;
static int init_chrdev(void)
{
+ driver.dev = &lirc_sir_dev->dev;
driver.minor = lirc_register_driver(&driver);
if (driver.minor < 0) {
printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
@@ -1215,20 +1218,71 @@ static int init_lirc_sir(void)
return 0;
}
+static int __devinit lirc_sir_probe(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int __devexit lirc_sir_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver lirc_sir_driver = {
+ .probe = lirc_sir_probe,
+ .remove = __devexit_p(lirc_sir_remove),
+ .driver = {
+ .name = "lirc_sir",
+ .owner = THIS_MODULE,
+ },
+};
static int __init lirc_sir_init(void)
{
int retval;
+ retval = platform_driver_register(&lirc_sir_driver);
+ if (retval) {
+ printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register "
+ "failed!\n");
+ return -ENODEV;
+ }
+
+ lirc_sir_dev = platform_device_alloc("lirc_dev", 0);
+ if (!lirc_sir_dev) {
+ printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc "
+ "failed!\n");
+ retval = -ENOMEM;
+ goto pdev_alloc_fail;
+ }
+
+ retval = platform_device_add(lirc_sir_dev);
+ if (retval) {
+ printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add "
+ "failed!\n");
+ retval = -ENODEV;
+ goto pdev_add_fail;
+ }
+
retval = init_chrdev();
if (retval < 0)
- return retval;
+ goto fail;
+
retval = init_lirc_sir();
if (retval) {
drop_chrdev();
- return retval;
+ goto fail;
}
+
return 0;
+
+fail:
+ platform_device_del(lirc_sir_dev);
+pdev_add_fail:
+ platform_device_put(lirc_sir_dev);
+pdev_alloc_fail:
+ platform_driver_unregister(&lirc_sir_driver);
+ return retval;
}
static void __exit lirc_sir_exit(void)
@@ -1236,6 +1290,8 @@ static void __exit lirc_sir_exit(void)
drop_hardware();
drop_chrdev();
drop_port();
+ platform_device_unregister(lirc_sir_dev);
+ platform_driver_unregister(&lirc_sir_driver);
printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
}
diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO
index 7e6c4fa130df..539f739fe9e6 100644
--- a/drivers/staging/media/solo6x10/TODO
+++ b/drivers/staging/media/solo6x10/TODO
@@ -20,5 +20,5 @@ TODO (general):
- implement loopback of external sound jack with incoming audio?
- implement pause/resume
-Plase send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc Ben Collins
+Plase send patches to Mauro Carvalho Chehab <mchehab@redhat.com> and Cc Ben Collins
<bcollins@bluecherry.net>
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index d2fd842e37cf..3ee9b125797f 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -318,15 +318,4 @@ static struct pci_driver solo_pci_driver = {
.remove = solo_pci_remove,
};
-static int __init solo_module_init(void)
-{
- return pci_register_driver(&solo_pci_driver);
-}
-
-static void __exit solo_module_exit(void)
-{
- pci_unregister_driver(&solo_pci_driver);
-}
-
-module_init(solo_module_init);
-module_exit(solo_module_exit);
+module_pci_driver(solo_pci_driver);
diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c
index ef95a500b4da..398070a3d293 100644
--- a/drivers/staging/media/solo6x10/i2c.c
+++ b/drivers/staging/media/solo6x10/i2c.c
@@ -175,7 +175,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev)
solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC);
- if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) ||
+ if (status & (SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) ||
solo_dev->i2c_id < 0) {
solo_i2c_stop(solo_dev);
return -ENXIO;
diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c
index e31949c9c87e..f15b31b37ca5 100644
--- a/drivers/staging/octeon/ethernet-mdio.c
+++ b/drivers/staging/octeon/ethernet-mdio.c
@@ -28,6 +28,7 @@
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/ratelimit.h>
+#include <linux/of_mdio.h>
#include <net/dst.h>
@@ -161,22 +162,23 @@ static void cvm_oct_adjust_link(struct net_device *dev)
int cvm_oct_phy_setup_device(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
+ struct device_node *phy_node;
- int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
- if (phy_addr != -1) {
- char phy_id[MII_BUS_ID_SIZE + 3];
+ if (!priv->of_node)
+ return 0;
- snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", phy_addr);
+ phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0);
+ if (!phy_node)
+ return 0;
- priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
- PHY_INTERFACE_MODE_GMII);
+ priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0,
+ PHY_INTERFACE_MODE_GMII);
+
+ if (priv->phydev == NULL)
+ return -ENODEV;
+
+ priv->last_link = 0;
+ phy_start_aneg(priv->phydev);
- if (IS_ERR(priv->phydev)) {
- priv->phydev = NULL;
- return -1;
- }
- priv->last_link = 0;
- phy_start_aneg(priv->phydev);
- }
return 0;
}
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
index 18f7a790f73d..683bedc74dde 100644
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -24,6 +24,7 @@
* This file may also be available under a different license from Cavium.
* Contact Cavium Networks for more information
**********************************************************************/
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -32,6 +33,7 @@
#include <linux/phy.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/of_net.h>
#include <net/dst.h>
@@ -113,15 +115,6 @@ int rx_napi_weight = 32;
module_param(rx_napi_weight, int, 0444);
MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
-/*
- * The offset from mac_addr_base that should be used for the next port
- * that is configured. By convention, if any mgmt ports exist on the
- * chip, they get the first mac addresses, The ports controlled by
- * this driver are numbered sequencially following any mgmt addresses
- * that may exist.
- */
-static unsigned int cvm_oct_mac_addr_offset;
-
/**
* cvm_oct_poll_queue - Workqueue for polling operations.
*/
@@ -176,7 +169,7 @@ static void cvm_oct_periodic_worker(struct work_struct *work)
queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
}
-static __init void cvm_oct_configure_common_hw(void)
+static __devinit void cvm_oct_configure_common_hw(void)
{
/* Setup the FPA */
cvmx_fpa_enable();
@@ -396,23 +389,21 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev)
* Returns Zero on success
*/
-static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
+static int cvm_oct_set_mac_filter(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
union cvmx_gmxx_prtx_cfg gmx_cfg;
int interface = INTERFACE(priv->port);
int index = INDEX(priv->port);
- memcpy(dev->dev_addr, addr + 2, 6);
-
if ((interface < 2)
&& (cvmx_helper_interface_get_mode(interface) !=
CVMX_HELPER_INTERFACE_MODE_SPI)) {
int i;
- uint8_t *ptr = addr;
+ uint8_t *ptr = dev->dev_addr;
uint64_t mac = 0;
for (i = 0; i < 6; i++)
- mac = (mac << 8) | (uint64_t) (ptr[i + 2]);
+ mac = (mac << 8) | (uint64_t)ptr[i];
gmx_cfg.u64 =
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
@@ -421,17 +412,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
- ptr[2]);
+ ptr[0]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
- ptr[3]);
+ ptr[1]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
- ptr[4]);
+ ptr[2]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
- ptr[5]);
+ ptr[3]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
- ptr[6]);
+ ptr[4]);
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
- ptr[7]);
+ ptr[5]);
cvm_oct_common_set_multicast_list(dev);
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
gmx_cfg.u64);
@@ -439,6 +430,15 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
return 0;
}
+static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
+{
+ int r = eth_mac_addr(dev, addr);
+
+ if (r)
+ return r;
+ return cvm_oct_set_mac_filter(dev);
+}
+
/**
* cvm_oct_common_init - per network device initialization
* @dev: Device to initialize
@@ -448,26 +448,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
int cvm_oct_common_init(struct net_device *dev)
{
struct octeon_ethernet *priv = netdev_priv(dev);
- struct sockaddr sa;
- u64 mac = ((u64)(octeon_bootinfo->mac_addr_base[0] & 0xff) << 40) |
- ((u64)(octeon_bootinfo->mac_addr_base[1] & 0xff) << 32) |
- ((u64)(octeon_bootinfo->mac_addr_base[2] & 0xff) << 24) |
- ((u64)(octeon_bootinfo->mac_addr_base[3] & 0xff) << 16) |
- ((u64)(octeon_bootinfo->mac_addr_base[4] & 0xff) << 8) |
- (u64)(octeon_bootinfo->mac_addr_base[5] & 0xff);
-
- mac += cvm_oct_mac_addr_offset;
- sa.sa_data[0] = (mac >> 40) & 0xff;
- sa.sa_data[1] = (mac >> 32) & 0xff;
- sa.sa_data[2] = (mac >> 24) & 0xff;
- sa.sa_data[3] = (mac >> 16) & 0xff;
- sa.sa_data[4] = (mac >> 8) & 0xff;
- sa.sa_data[5] = mac & 0xff;
-
- if (cvm_oct_mac_addr_offset >= octeon_bootinfo->mac_addr_count)
- printk(KERN_DEBUG "%s: Using MAC outside of the assigned range:"
- " %pM\n", dev->name, sa.sa_data);
- cvm_oct_mac_addr_offset++;
+ const u8 *mac = NULL;
+
+ if (priv->of_node)
+ mac = of_get_mac_address(priv->of_node);
+
+ if (mac && is_valid_ether_addr(mac)) {
+ memcpy(dev->dev_addr, mac, ETH_ALEN);
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
+ } else {
+ eth_hw_addr_random(dev);
+ }
/*
* Force the interface to use the POW send if always_use_pow
@@ -488,7 +479,7 @@ int cvm_oct_common_init(struct net_device *dev)
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
cvm_oct_phy_setup_device(dev);
- dev->netdev_ops->ndo_set_mac_address(dev, &sa);
+ cvm_oct_set_mac_filter(dev);
dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
/*
@@ -595,22 +586,55 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
extern void octeon_mdiobus_force_mod_depencency(void);
-static int __init cvm_oct_init_module(void)
+static struct device_node * __devinit cvm_oct_of_get_child(const struct device_node *parent,
+ int reg_val)
+{
+ struct device_node *node = NULL;
+ int size;
+ const __be32 *addr;
+
+ for (;;) {
+ node = of_get_next_child(parent, node);
+ if (!node)
+ break;
+ addr = of_get_property(node, "reg", &size);
+ if (addr && (be32_to_cpu(*addr) == reg_val))
+ break;
+ }
+ return node;
+}
+
+static struct device_node * __devinit cvm_oct_node_for_port(struct device_node *pip,
+ int interface, int port)
+{
+ struct device_node *ni, *np;
+
+ ni = cvm_oct_of_get_child(pip, interface);
+ if (!ni)
+ return NULL;
+
+ np = cvm_oct_of_get_child(ni, port);
+ of_node_put(ni);
+
+ return np;
+}
+
+static int __devinit cvm_oct_probe(struct platform_device *pdev)
{
int num_interfaces;
int interface;
int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
int qos;
+ struct device_node *pip;
octeon_mdiobus_force_mod_depencency();
pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
- if (OCTEON_IS_MODEL(OCTEON_CN52XX))
- cvm_oct_mac_addr_offset = 2; /* First two are the mgmt ports. */
- else if (OCTEON_IS_MODEL(OCTEON_CN56XX))
- cvm_oct_mac_addr_offset = 1; /* First one is the mgmt port. */
- else
- cvm_oct_mac_addr_offset = 0;
+ pip = pdev->dev.of_node;
+ if (!pip) {
+ pr_err("Error: No 'pip' in /aliases\n");
+ return -EINVAL;
+ }
cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
if (cvm_oct_poll_queue == NULL) {
@@ -689,10 +713,11 @@ static int __init cvm_oct_init_module(void)
cvmx_helper_interface_get_mode(interface);
int num_ports = cvmx_helper_ports_on_interface(interface);
int port;
+ int port_index;
- for (port = cvmx_helper_get_ipd_port(interface, 0);
+ for (port_index = 0, port = cvmx_helper_get_ipd_port(interface, 0);
port < cvmx_helper_get_ipd_port(interface, num_ports);
- port++) {
+ port_index++, port++) {
struct octeon_ethernet *priv;
struct net_device *dev =
alloc_etherdev(sizeof(struct octeon_ethernet));
@@ -703,6 +728,7 @@ static int __init cvm_oct_init_module(void)
/* Initialize the device private structure. */
priv = netdev_priv(dev);
+ priv->of_node = cvm_oct_node_for_port(pip, interface, port_index);
INIT_DELAYED_WORK(&priv->port_periodic_work,
cvm_oct_periodic_worker);
@@ -787,7 +813,7 @@ static int __init cvm_oct_init_module(void)
return 0;
}
-static void __exit cvm_oct_cleanup_module(void)
+static int __devexit cvm_oct_remove(struct platform_device *pdev)
{
int port;
@@ -835,10 +861,29 @@ static void __exit cvm_oct_cleanup_module(void)
if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+ return 0;
}
+static struct of_device_id cvm_oct_match[] = {
+ {
+ .compatible = "cavium,octeon-3860-pip",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cvm_oct_match);
+
+static struct platform_driver cvm_oct_driver = {
+ .probe = cvm_oct_probe,
+ .remove = __devexit_p(cvm_oct_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .of_match_table = cvm_oct_match,
+ },
+};
+
+module_platform_driver(cvm_oct_driver);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
-module_init(cvm_oct_init_module);
-module_exit(cvm_oct_cleanup_module);
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
index d58192563552..9360e22e0739 100644
--- a/drivers/staging/octeon/octeon-ethernet.h
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -31,6 +31,8 @@
#ifndef OCTEON_ETHERNET_H
#define OCTEON_ETHERNET_H
+#include <linux/of.h>
+
/**
* This is the definition of the Ethernet driver's private
* driver state stored in netdev_priv(dev).
@@ -59,6 +61,7 @@ struct octeon_ethernet {
void (*poll) (struct net_device *dev);
struct delayed_work port_periodic_work;
struct work_struct port_work; /* may be unused. */
+ struct device_node *of_node;
};
int cvm_oct_free_work(void *work_queue_entry);
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 9e2100551c78..cbb5aaf3e567 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -109,46 +109,29 @@ static struct se_device *fd_create_virtdevice(
struct se_subsystem_dev *se_dev,
void *p)
{
- char *dev_p = NULL;
struct se_device *dev;
struct se_dev_limits dev_limits;
struct queue_limits *limits;
struct fd_dev *fd_dev = p;
struct fd_host *fd_host = hba->hba_ptr;
- mm_segment_t old_fs;
struct file *file;
struct inode *inode = NULL;
int dev_flags = 0, flags, ret = -EINVAL;
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
- old_fs = get_fs();
- set_fs(get_ds());
- dev_p = getname(fd_dev->fd_dev_name);
- set_fs(old_fs);
-
- if (IS_ERR(dev_p)) {
- pr_err("getname(%s) failed: %lu\n",
- fd_dev->fd_dev_name, IS_ERR(dev_p));
- ret = PTR_ERR(dev_p);
- goto fail;
- }
/*
* Use O_DSYNC by default instead of O_SYNC to forgo syncing
* of pure timestamp updates.
*/
flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC;
- file = filp_open(dev_p, flags, 0600);
+ file = filp_open(fd_dev->fd_dev_name, flags, 0600);
if (IS_ERR(file)) {
- pr_err("filp_open(%s) failed\n", dev_p);
+ pr_err("filp_open(%s) failed\n", fd_dev->fd_dev_name);
ret = PTR_ERR(file);
goto fail;
}
- if (!file || !file->f_dentry) {
- pr_err("filp_open(%s) failed\n", dev_p);
- goto fail;
- }
fd_dev->fd_file = file;
/*
* If using a block backend with this struct file, we extract
@@ -212,14 +195,12 @@ static struct se_device *fd_create_virtdevice(
" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
fd_dev->fd_dev_name, fd_dev->fd_dev_size);
- putname(dev_p);
return dev;
fail:
if (fd_dev->fd_file) {
filp_close(fd_dev->fd_file, NULL);
fd_dev->fd_file = NULL;
}
- putname(dev_p);
return ERR_PTR(ret);
}
@@ -452,14 +433,11 @@ static ssize_t fd_set_configfs_dev_params(
token = match_token(ptr, tokens, args);
switch (token) {
case Opt_fd_dev_name:
- arg_p = match_strdup(&args[0]);
- if (!arg_p) {
- ret = -ENOMEM;
+ if (match_strlcpy(fd_dev->fd_dev_name, &args[0],
+ FD_MAX_DEV_NAME) == 0) {
+ ret = -EINVAL;
break;
}
- snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
- "%s", arg_p);
- kfree(arg_p);
pr_debug("FILEIO: Referencing Path: %s\n",
fd_dev->fd_dev_name);
fd_dev->fbd_flags |= FBDF_HAS_PATH;
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 2d7a9fe8f365..2ab31e4f02cc 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -1251,7 +1251,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
* longer needed. The passive cooling formula uses tc1 and tc2 as described in
* section 11.1.5.1 of the ACPI specification 3.0.
*/
-struct thermal_zone_device *thermal_zone_device_register(char *type,
+struct thermal_zone_device *thermal_zone_device_register(const char *type,
int trips, int mask, void *devdata,
const struct thermal_zone_device_ops *ops,
int tc1, int tc2, int passive_delay, int polling_delay)
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 6cd414341d5e..6579ffdd8e9b 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -216,8 +216,7 @@ static int ulite_startup(struct uart_port *port)
{
int ret;
- ret = request_irq(port->irq, ulite_isr,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+ ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port);
if (ret)
return ret;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 821126eb8176..128a804c42f4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -25,6 +25,7 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/random.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -2181,6 +2182,14 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */
announce_device(udev);
+ if (udev->serial)
+ add_device_randomness(udev->serial, strlen(udev->serial));
+ if (udev->product)
+ add_device_randomness(udev->product, strlen(udev->product));
+ if (udev->manufacturer)
+ add_device_randomness(udev->manufacturer,
+ strlen(udev->manufacturer));
+
device_enable_async_suspend(&udev->dev);
/*
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index 965a6293206a..8ee9268fe253 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
struct page *page;
int err;
- page = alloc_page(gfp_flags);
+ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 3d28fb976c78..9fd7886cfa9a 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1836,7 +1836,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* init to known state, then setup irqs */
udc_reset(dev);
udc_reinit (dev);
- if (request_irq(pdev->irq, goku_irq, IRQF_SHARED/*|IRQF_SAMPLE_RANDOM*/,
+ if (request_irq(pdev->irq, goku_irq, IRQF_SHARED,
driver_name, dev) != 0) {
DBG(dev, "request interrupt %d failed\n", pdev->irq);
retval = -EBUSY;
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 8981fbb5748c..cf6bd626f3fe 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -1583,12 +1583,10 @@ static int __exit m66592_remove(struct platform_device *pdev)
iounmap(m66592->reg);
free_irq(platform_get_irq(pdev, 0), m66592);
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);
clk_put(m66592->clk);
}
-#endif
kfree(m66592);
return 0;
}
@@ -1602,9 +1600,7 @@ static int __init m66592_probe(struct platform_device *pdev)
struct resource *res, *ires;
void __iomem *reg = NULL;
struct m66592 *m66592 = NULL;
-#ifdef CONFIG_HAVE_CLK
char clk_name[8];
-#endif
int ret = 0;
int i;
@@ -1671,7 +1667,6 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
m66592->clk = clk_get(&pdev->dev, clk_name);
@@ -1683,7 +1678,7 @@ static int __init m66592_probe(struct platform_device *pdev)
}
clk_enable(m66592->clk);
}
-#endif
+
INIT_LIST_HEAD(&m66592->gadget.ep_list);
m66592->gadget.ep0 = &m66592->ep[0].ep;
INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
@@ -1731,13 +1726,11 @@ err_add_udc:
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
clean_up3:
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);
clk_put(m66592->clk);
}
clean_up2:
-#endif
free_irq(ires->start, m66592);
clean_up:
if (m66592) {
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
index 88c85b4116a2..16c7e14678b8 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -13,10 +13,7 @@
#ifndef __M66592_UDC_H__
#define __M66592_UDC_H__
-#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
-#endif
-
#include <linux/usb/m66592.h>
#define M66592_SYSCFG 0x00
@@ -468,9 +465,7 @@ struct m66592_ep {
struct m66592 {
spinlock_t lock;
void __iomem *reg;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
-#endif
struct m66592_platdata *pdata;
unsigned long irq_trigger;
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 53c093b941e5..907ad3ecb341 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -2201,19 +2201,15 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
#ifdef CONFIG_ARCH_LUBBOCK
if (machine_is_lubbock()) {
- retval = request_irq(LUBBOCK_USB_DISC_IRQ,
- lubbock_vbus_irq,
- IRQF_SAMPLE_RANDOM,
- driver_name, dev);
+ retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq,
+ 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
goto err_irq_lub;
}
- retval = request_irq(LUBBOCK_USB_IRQ,
- lubbock_vbus_irq,
- IRQF_SAMPLE_RANDOM,
- driver_name, dev);
+ retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq,
+ 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_IRQ, retval);
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index f3ac2a20c27c..5a80751accb7 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -1831,12 +1831,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
iounmap(r8a66597->sudmac_reg);
free_irq(platform_get_irq(pdev, 0), r8a66597);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
-#ifdef CONFIG_HAVE_CLK
+
if (r8a66597->pdata->on_chip) {
clk_disable(r8a66597->clk);
clk_put(r8a66597->clk);
}
-#endif
+
device_unregister(&r8a66597->gadget.dev);
kfree(r8a66597);
return 0;
@@ -1868,9 +1868,7 @@ static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
static int __init r8a66597_probe(struct platform_device *pdev)
{
-#ifdef CONFIG_HAVE_CLK
char clk_name[8];
-#endif
struct resource *res, *ires;
int irq;
void __iomem *reg = NULL;
@@ -1934,7 +1932,6 @@ static int __init r8a66597_probe(struct platform_device *pdev)
r8a66597->timer.data = (unsigned long)r8a66597;
r8a66597->reg = reg;
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
r8a66597->clk = clk_get(&pdev->dev, clk_name);
@@ -1946,7 +1943,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
}
clk_enable(r8a66597->clk);
}
-#endif
+
if (r8a66597->pdata->sudmac) {
ret = r8a66597_sudmac_ioremap(r8a66597, pdev);
if (ret < 0)
@@ -2006,13 +2003,11 @@ err_add_udc:
clean_up3:
free_irq(irq, r8a66597);
clean_up2:
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip) {
clk_disable(r8a66597->clk);
clk_put(r8a66597->clk);
}
clean_up_dev:
-#endif
device_unregister(&r8a66597->gadget.dev);
clean_up:
if (r8a66597) {
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h
index 99908c76ccd1..45c4b2df1785 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/r8a66597-udc.h
@@ -13,10 +13,7 @@
#ifndef __R8A66597_H__
#define __R8A66597_H__
-#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
-#endif
-
#include <linux/usb/r8a66597.h>
#define R8A66597_MAX_SAMPLING 10
@@ -92,9 +89,7 @@ struct r8a66597 {
void __iomem *reg;
void __iomem *sudmac_reg;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
-#endif
struct r8a66597_platdata *pdata;
struct usb_gadget gadget;
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index ae8b18869b8c..8d9bcd8207c8 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -656,9 +656,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (!(filp->f_mode & FMODE_WRITE))
ro = 1;
- if (filp->f_path.dentry)
- inode = filp->f_path.dentry->d_inode;
- if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ inode = filp->f_path.dentry->d_inode;
+ if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
LINFO(curlun, "invalid file type: %s\n", filename);
goto out;
}
@@ -667,7 +666,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
* If we can't read the file, it's no good.
* If we can't write the file, use it read-only.
*/
- if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
+ if (!(filp->f_op->read || filp->f_op->aio_read)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
}
@@ -712,7 +711,6 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (fsg_lun_is_open(curlun))
fsg_lun_close(curlun);
- get_file(filp);
curlun->blksize = blksize;
curlun->blkbits = blkbits;
curlun->ro = ro;
@@ -720,10 +718,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
curlun->file_length = size;
curlun->num_sectors = num_sectors;
LDBG(curlun, "open backing file: %s\n", filename);
- rc = 0;
+ return 0;
out:
- filp_close(filp, current->files);
+ fput(filp);
return rc;
}
diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c
index af9898982059..e0c5e88e03ed 100644
--- a/drivers/usb/gadget/u_uac1.c
+++ b/drivers/usb/gadget/u_uac1.c
@@ -275,17 +275,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
/* Close control device */
snd = &gau->control;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM playback device and setup substream */
snd = &gau->playback;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM capture device and setup substream */
snd = &gau->capture;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
return 0;
}
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index ec21f4a4a056..bb55eb4a7d48 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -152,14 +152,14 @@ static int omap_ehci_init(struct usb_hcd *hcd)
struct ehci_hcd_omap_platform_data *pdata;
pdata = hcd->self.controller->platform_data;
+
+ /* Hold PHYs in reset while initializing EHCI controller */
if (pdata->phy_reset) {
if (gpio_is_valid(pdata->reset_gpio_port[0]))
- gpio_request_one(pdata->reset_gpio_port[0],
- GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
+ gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
if (gpio_is_valid(pdata->reset_gpio_port[1]))
- gpio_request_one(pdata->reset_gpio_port[1],
- GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
+ gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
/* Hold the PHY in RESET for enough time till DIR is high */
udelay(10);
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index c868be65e763..4c634eb56358 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -95,9 +95,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
int i = 0;
if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
clk_enable(r8a66597->clk);
-#endif
do {
r8a66597_write(r8a66597, SCKE, SYSCFG0);
tmp = r8a66597_read(r8a66597, SYSCFG0);
@@ -141,9 +139,7 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
udelay(1);
if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
clk_disable(r8a66597->clk);
-#endif
} else {
r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
@@ -2406,19 +2402,15 @@ static int __devexit r8a66597_remove(struct platform_device *pdev)
del_timer_sync(&r8a66597->rh_timer);
usb_remove_hcd(hcd);
iounmap(r8a66597->reg);
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip)
clk_put(r8a66597->clk);
-#endif
usb_put_hcd(hcd);
return 0;
}
static int __devinit r8a66597_probe(struct platform_device *pdev)
{
-#ifdef CONFIG_HAVE_CLK
char clk_name[8];
-#endif
struct resource *res = NULL, *ires;
int irq = -1;
void __iomem *reg = NULL;
@@ -2482,7 +2474,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
if (r8a66597->pdata->on_chip) {
-#ifdef CONFIG_HAVE_CLK
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
r8a66597->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(r8a66597->clk)) {
@@ -2491,7 +2482,6 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
ret = PTR_ERR(r8a66597->clk);
goto clean_up2;
}
-#endif
r8a66597->max_root_hub = 1;
} else
r8a66597->max_root_hub = 2;
@@ -2531,11 +2521,9 @@ static int __devinit r8a66597_probe(struct platform_device *pdev)
return 0;
clean_up3:
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip)
clk_put(r8a66597->clk);
clean_up2:
-#endif
usb_put_hcd(hcd);
clean_up:
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index f28782d20eef..672cea307abb 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -26,10 +26,7 @@
#ifndef __R8A66597_H__
#define __R8A66597_H__
-#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
-#endif
-
#include <linux/usb/r8a66597.h>
#define R8A66597_MAX_NUM_PIPE 10
@@ -113,9 +110,7 @@ struct r8a66597_root_hub {
struct r8a66597 {
spinlock_t lock;
void __iomem *reg;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
-#endif
struct r8a66597_platdata *pdata;
struct r8a66597_device device0;
struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB];
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index dbcdeea30f09..586105b55a7c 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -81,14 +81,6 @@ struct musb_ep;
#define is_peripheral_active(m) (!(m)->is_host)
#define is_host_active(m) ((m)->is_host)
-#ifndef CONFIG_HAVE_CLK
-/* Dummy stub for clk framework */
-#define clk_get(dev, id) NULL
-#define clk_put(clock) do {} while (0)
-#define clk_enable(clock) do {} while (0)
-#define clk_disable(clock) do {} while (0)
-#endif
-
#ifdef CONFIG_PROC_FS
#include <linux/fs.h>
#define MUSB_CONFIG_PROC_FS
diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c
index 575fc815c932..7a88667742b6 100644
--- a/drivers/usb/otg/isp1301_omap.c
+++ b/drivers/usb/otg/isp1301_omap.c
@@ -1576,7 +1576,6 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
isp->irq_type = IRQF_TRIGGER_FALLING;
}
- isp->irq_type |= IRQF_SAMPLE_RANDOM;
status = request_irq(i2c->irq, isp1301_irq,
isp->irq_type, DRIVER_NAME, isp);
if (status < 0) {
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
new file mode 100644
index 000000000000..7cd5dec0abd1
--- /dev/null
+++ b/drivers/vfio/Kconfig
@@ -0,0 +1,16 @@
+config VFIO_IOMMU_TYPE1
+ tristate
+ depends on VFIO
+ default n
+
+menuconfig VFIO
+ tristate "VFIO Non-Privileged userspace driver framework"
+ depends on IOMMU_API
+ select VFIO_IOMMU_TYPE1 if X86
+ help
+ VFIO provides a framework for secure userspace device drivers.
+ See Documentation/vfio.txt for more details.
+
+ If you don't know what to do here, say N.
+
+source "drivers/vfio/pci/Kconfig"
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
new file mode 100644
index 000000000000..2398d4a0e38b
--- /dev/null
+++ b/drivers/vfio/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VFIO) += vfio.o
+obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
+obj-$(CONFIG_VFIO_PCI) += pci/
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig
new file mode 100644
index 000000000000..5980758563eb
--- /dev/null
+++ b/drivers/vfio/pci/Kconfig
@@ -0,0 +1,8 @@
+config VFIO_PCI
+ tristate "VFIO support for PCI devices"
+ depends on VFIO && PCI && EVENTFD
+ help
+ Support for the PCI VFIO bus driver. This is required to make
+ use of PCI drivers using the VFIO framework.
+
+ If you don't know what to do here, say N.
diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile
new file mode 100644
index 000000000000..131079255fd9
--- /dev/null
+++ b/drivers/vfio/pci/Makefile
@@ -0,0 +1,4 @@
+
+vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
+
+obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
new file mode 100644
index 000000000000..6968b7232232
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/eventfd.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "VFIO PCI - User Level meta-driver"
+
+static bool nointxmask;
+module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nointxmask,
+ "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag.");
+
+static int vfio_pci_enable(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+ u16 cmd;
+ u8 msix_pos;
+
+ vdev->reset_works = (pci_reset_function(pdev) == 0);
+ pci_save_state(pdev);
+ vdev->pci_saved_state = pci_store_saved_state(pdev);
+ if (!vdev->pci_saved_state)
+ pr_debug("%s: Couldn't store %s saved state\n",
+ __func__, dev_name(&pdev->dev));
+
+ ret = vfio_config_init(vdev);
+ if (ret)
+ goto out;
+
+ if (likely(!nointxmask))
+ vdev->pci_2_3 = pci_intx_mask_supported(pdev);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
+ cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ }
+
+ msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+ if (msix_pos) {
+ u16 flags;
+ u32 table;
+
+ pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags);
+ pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table);
+
+ vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16;
+ } else
+ vdev->msix_bar = 0xFF;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out;
+
+ return ret;
+
+out:
+ kfree(vdev->pci_saved_state);
+ vdev->pci_saved_state = NULL;
+ vfio_config_free(vdev);
+ return ret;
+}
+
+static void vfio_pci_disable(struct vfio_pci_device *vdev)
+{
+ int bar;
+
+ pci_disable_device(vdev->pdev);
+
+ vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ vdev->irq_type, 0, 0, NULL);
+
+ vdev->virq_disabled = false;
+
+ vfio_config_free(vdev);
+
+ pci_reset_function(vdev->pdev);
+
+ if (pci_load_and_free_saved_state(vdev->pdev,
+ &vdev->pci_saved_state) == 0)
+ pci_restore_state(vdev->pdev);
+ else
+ pr_info("%s: Couldn't reload %s saved state\n",
+ __func__, dev_name(&vdev->pdev->dev));
+
+ for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) {
+ if (!vdev->barmap[bar])
+ continue;
+ pci_iounmap(vdev->pdev, vdev->barmap[bar]);
+ pci_release_selected_regions(vdev->pdev, 1 << bar);
+ vdev->barmap[bar] = NULL;
+ }
+}
+
+static void vfio_pci_release(void *device_data)
+{
+ struct vfio_pci_device *vdev = device_data;
+
+ if (atomic_dec_and_test(&vdev->refcnt))
+ vfio_pci_disable(vdev);
+
+ module_put(THIS_MODULE);
+}
+
+static int vfio_pci_open(void *device_data)
+{
+ struct vfio_pci_device *vdev = device_data;
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ if (atomic_inc_return(&vdev->refcnt) == 1) {
+ int ret = vfio_pci_enable(vdev);
+ if (ret) {
+ module_put(THIS_MODULE);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
+{
+ if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
+ u8 pin;
+ pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
+ if (pin)
+ return 1;
+
+ } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
+ u8 pos;
+ u16 flags;
+
+ pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI);
+ if (pos) {
+ pci_read_config_word(vdev->pdev,
+ pos + PCI_MSI_FLAGS, &flags);
+
+ return 1 << (flags & PCI_MSI_FLAGS_QMASK);
+ }
+ } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) {
+ u8 pos;
+ u16 flags;
+
+ pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX);
+ if (pos) {
+ pci_read_config_word(vdev->pdev,
+ pos + PCI_MSIX_FLAGS, &flags);
+
+ return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
+ }
+ }
+
+ return 0;
+}
+
+static long vfio_pci_ioctl(void *device_data,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_pci_device *vdev = device_data;
+ unsigned long minsz;
+
+ if (cmd == VFIO_DEVICE_GET_INFO) {
+ struct vfio_device_info info;
+
+ minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = VFIO_DEVICE_FLAGS_PCI;
+
+ if (vdev->reset_works)
+ info.flags |= VFIO_DEVICE_FLAGS_RESET;
+
+ info.num_regions = VFIO_PCI_NUM_REGIONS;
+ info.num_irqs = VFIO_PCI_NUM_IRQS;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
+ struct pci_dev *pdev = vdev->pdev;
+ struct vfio_region_info info;
+
+ minsz = offsetofend(struct vfio_region_info, offset);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ switch (info.index) {
+ case VFIO_PCI_CONFIG_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = pdev->cfg_size;
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+ case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = pci_resource_len(pdev, info.index);
+ if (!info.size) {
+ info.flags = 0;
+ break;
+ }
+
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ if (pci_resource_flags(pdev, info.index) &
+ IORESOURCE_MEM && info.size >= PAGE_SIZE)
+ info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
+ break;
+ case VFIO_PCI_ROM_REGION_INDEX:
+ {
+ void __iomem *io;
+ size_t size;
+
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.flags = 0;
+
+ /* Report the BAR size, not the ROM size */
+ info.size = pci_resource_len(pdev, info.index);
+ if (!info.size)
+ break;
+
+ /* Is it really there? */
+ io = pci_map_rom(pdev, &size);
+ if (!io || !size) {
+ info.size = 0;
+ break;
+ }
+ pci_unmap_rom(pdev, io);
+
+ info.flags = VFIO_REGION_INFO_FLAG_READ;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
+ struct vfio_irq_info info;
+
+ minsz = offsetofend(struct vfio_irq_info, count);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+ return -EINVAL;
+
+ info.flags = VFIO_IRQ_INFO_EVENTFD;
+
+ info.count = vfio_pci_get_irq_count(vdev, info.index);
+
+ if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+ info.flags |= (VFIO_IRQ_INFO_MASKABLE |
+ VFIO_IRQ_INFO_AUTOMASKED);
+ else
+ info.flags |= VFIO_IRQ_INFO_NORESIZE;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_SET_IRQS) {
+ struct vfio_irq_set hdr;
+ u8 *data = NULL;
+ int ret = 0;
+
+ minsz = offsetofend(struct vfio_irq_set, count);
+
+ if (copy_from_user(&hdr, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
+ VFIO_IRQ_SET_ACTION_TYPE_MASK))
+ return -EINVAL;
+
+ if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
+ size_t size;
+
+ if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
+ size = sizeof(uint8_t);
+ else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
+ size = sizeof(int32_t);
+ else
+ return -EINVAL;
+
+ if (hdr.argsz - minsz < hdr.count * size ||
+ hdr.count > vfio_pci_get_irq_count(vdev, hdr.index))
+ return -EINVAL;
+
+ data = kmalloc(hdr.count * size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, (void __user *)(arg + minsz),
+ hdr.count * size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ }
+
+ mutex_lock(&vdev->igate);
+
+ ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
+ hdr.start, hdr.count, data);
+
+ mutex_unlock(&vdev->igate);
+ kfree(data);
+
+ return ret;
+
+ } else if (cmd == VFIO_DEVICE_RESET)
+ return vdev->reset_works ?
+ pci_reset_function(vdev->pdev) : -EINVAL;
+
+ return -ENOTTY;
+}
+
+static ssize_t vfio_pci_read(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+
+ if (index >= VFIO_PCI_NUM_REGIONS)
+ return -EINVAL;
+
+ if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+ return vfio_pci_config_readwrite(vdev, buf, count, ppos, false);
+ else if (index == VFIO_PCI_ROM_REGION_INDEX)
+ return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+ return vfio_pci_io_readwrite(vdev, buf, count, ppos, false);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM)
+ return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+
+ return -EINVAL;
+}
+
+static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+
+ if (index >= VFIO_PCI_NUM_REGIONS)
+ return -EINVAL;
+
+ if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+ return vfio_pci_config_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ else if (index == VFIO_PCI_ROM_REGION_INDEX)
+ return -EINVAL;
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+ return vfio_pci_io_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) {
+ return vfio_pci_mem_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ }
+
+ return -EINVAL;
+}
+
+static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
+{
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned int index;
+ u64 phys_len, req_len, pgoff, req_start, phys;
+ int ret;
+
+ index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+
+ if (vma->vm_end < vma->vm_start)
+ return -EINVAL;
+ if ((vma->vm_flags & VM_SHARED) == 0)
+ return -EINVAL;
+ if (index >= VFIO_PCI_ROM_REGION_INDEX)
+ return -EINVAL;
+ if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM))
+ return -EINVAL;
+
+ phys_len = pci_resource_len(pdev, index);
+ req_len = vma->vm_end - vma->vm_start;
+ pgoff = vma->vm_pgoff &
+ ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+ req_start = pgoff << PAGE_SHIFT;
+
+ if (phys_len < PAGE_SIZE || req_start + req_len > phys_len)
+ return -EINVAL;
+
+ if (index == vdev->msix_bar) {
+ /*
+ * Disallow mmaps overlapping the MSI-X table; users don't
+ * get to touch this directly. We could find somewhere
+ * else to map the overlap, but page granularity is only
+ * a recommendation, not a requirement, so the user needs
+ * to know which bits are real. Requiring them to mmap
+ * around the table makes that clear.
+ */
+
+ /* If neither entirely above nor below, then it overlaps */
+ if (!(req_start >= vdev->msix_offset + vdev->msix_size ||
+ req_start + req_len <= vdev->msix_offset))
+ return -EINVAL;
+ }
+
+ /*
+ * Even though we don't make use of the barmap for the mmap,
+ * we need to request the region and the barmap tracks that.
+ */
+ if (!vdev->barmap[index]) {
+ ret = pci_request_selected_regions(pdev,
+ 1 << index, "vfio-pci");
+ if (ret)
+ return ret;
+
+ vdev->barmap[index] = pci_iomap(pdev, index, 0);
+ }
+
+ vma->vm_private_data = vdev;
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff;
+
+ return remap_pfn_range(vma, vma->vm_start, phys,
+ req_len, vma->vm_page_prot);
+}
+
+static const struct vfio_device_ops vfio_pci_ops = {
+ .name = "vfio-pci",
+ .open = vfio_pci_open,
+ .release = vfio_pci_release,
+ .ioctl = vfio_pci_ioctl,
+ .read = vfio_pci_read,
+ .write = vfio_pci_write,
+ .mmap = vfio_pci_mmap,
+};
+
+static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ u8 type;
+ struct vfio_pci_device *vdev;
+ struct iommu_group *group;
+ int ret;
+
+ pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type);
+ if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL)
+ return -EINVAL;
+
+ group = iommu_group_get(&pdev->dev);
+ if (!group)
+ return -EINVAL;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ iommu_group_put(group);
+ return -ENOMEM;
+ }
+
+ vdev->pdev = pdev;
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ mutex_init(&vdev->igate);
+ spin_lock_init(&vdev->irqlock);
+ atomic_set(&vdev->refcnt, 0);
+
+ ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
+ if (ret) {
+ iommu_group_put(group);
+ kfree(vdev);
+ }
+
+ return ret;
+}
+
+static void vfio_pci_remove(struct pci_dev *pdev)
+{
+ struct vfio_pci_device *vdev;
+
+ vdev = vfio_del_group_dev(&pdev->dev);
+ if (!vdev)
+ return;
+
+ iommu_group_put(pdev->dev.iommu_group);
+ kfree(vdev);
+}
+
+static struct pci_driver vfio_pci_driver = {
+ .name = "vfio-pci",
+ .id_table = NULL, /* only dynamic ids */
+ .probe = vfio_pci_probe,
+ .remove = vfio_pci_remove,
+};
+
+static void __exit vfio_pci_cleanup(void)
+{
+ pci_unregister_driver(&vfio_pci_driver);
+ vfio_pci_virqfd_exit();
+ vfio_pci_uninit_perm_bits();
+}
+
+static int __init vfio_pci_init(void)
+{
+ int ret;
+
+ /* Allocate shared config space permision data used by all devices */
+ ret = vfio_pci_init_perm_bits();
+ if (ret)
+ return ret;
+
+ /* Start the virqfd cleanup handler */
+ ret = vfio_pci_virqfd_init();
+ if (ret)
+ goto out_virqfd;
+
+ /* Register and scan for devices */
+ ret = pci_register_driver(&vfio_pci_driver);
+ if (ret)
+ goto out_driver;
+
+ return 0;
+
+out_virqfd:
+ vfio_pci_virqfd_exit();
+out_driver:
+ vfio_pci_uninit_perm_bits();
+ return ret;
+}
+
+module_init(vfio_pci_init);
+module_exit(vfio_pci_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
new file mode 100644
index 000000000000..8b8f7d11e102
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -0,0 +1,1540 @@
+/*
+ * VFIO PCI config space virtualization
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+/*
+ * This code handles reading and writing of PCI configuration registers.
+ * This is hairy because we want to allow a lot of flexibility to the
+ * user driver, but cannot trust it with all of the config fields.
+ * Tables determine which fields can be read and written, as well as
+ * which fields are 'virtualized' - special actions and translations to
+ * make it appear to the user that he has control, when in fact things
+ * must be negotiated with the underlying OS.
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define PCI_CFG_SPACE_SIZE 256
+
+/* Useful "pseudo" capabilities */
+#define PCI_CAP_ID_BASIC 0
+#define PCI_CAP_ID_INVALID 0xFF
+
+#define is_bar(offset) \
+ ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \
+ (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4))
+
+/*
+ * Lengths of PCI Config Capabilities
+ * 0: Removed from the user visible capability list
+ * FF: Variable length
+ */
+static u8 pci_cap_length[] = {
+ [PCI_CAP_ID_BASIC] = PCI_STD_HEADER_SIZEOF, /* pci config header */
+ [PCI_CAP_ID_PM] = PCI_PM_SIZEOF,
+ [PCI_CAP_ID_AGP] = PCI_AGP_SIZEOF,
+ [PCI_CAP_ID_VPD] = PCI_CAP_VPD_SIZEOF,
+ [PCI_CAP_ID_SLOTID] = 0, /* bridge - don't care */
+ [PCI_CAP_ID_MSI] = 0xFF, /* 10, 14, 20, or 24 */
+ [PCI_CAP_ID_CHSWP] = 0, /* cpci - not yet */
+ [PCI_CAP_ID_PCIX] = 0xFF, /* 8 or 24 */
+ [PCI_CAP_ID_HT] = 0xFF, /* hypertransport */
+ [PCI_CAP_ID_VNDR] = 0xFF, /* variable */
+ [PCI_CAP_ID_DBG] = 0, /* debug - don't care */
+ [PCI_CAP_ID_CCRC] = 0, /* cpci - not yet */
+ [PCI_CAP_ID_SHPC] = 0, /* hotswap - not yet */
+ [PCI_CAP_ID_SSVID] = 0, /* bridge - don't care */
+ [PCI_CAP_ID_AGP3] = 0, /* AGP8x - not yet */
+ [PCI_CAP_ID_SECDEV] = 0, /* secure device not yet */
+ [PCI_CAP_ID_EXP] = 0xFF, /* 20 or 44 */
+ [PCI_CAP_ID_MSIX] = PCI_CAP_MSIX_SIZEOF,
+ [PCI_CAP_ID_SATA] = 0xFF,
+ [PCI_CAP_ID_AF] = PCI_CAP_AF_SIZEOF,
+};
+
+/*
+ * Lengths of PCIe/PCI-X Extended Config Capabilities
+ * 0: Removed or masked from the user visible capabilty list
+ * FF: Variable length
+ */
+static u16 pci_ext_cap_length[] = {
+ [PCI_EXT_CAP_ID_ERR] = PCI_ERR_ROOT_COMMAND,
+ [PCI_EXT_CAP_ID_VC] = 0xFF,
+ [PCI_EXT_CAP_ID_DSN] = PCI_EXT_CAP_DSN_SIZEOF,
+ [PCI_EXT_CAP_ID_PWR] = PCI_EXT_CAP_PWR_SIZEOF,
+ [PCI_EXT_CAP_ID_RCLD] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_RCILC] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_RCEC] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_MFVC] = 0xFF,
+ [PCI_EXT_CAP_ID_VC9] = 0xFF, /* same as CAP_ID_VC */
+ [PCI_EXT_CAP_ID_RCRB] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_VNDR] = 0xFF,
+ [PCI_EXT_CAP_ID_CAC] = 0, /* obsolete */
+ [PCI_EXT_CAP_ID_ACS] = 0xFF,
+ [PCI_EXT_CAP_ID_ARI] = PCI_EXT_CAP_ARI_SIZEOF,
+ [PCI_EXT_CAP_ID_ATS] = PCI_EXT_CAP_ATS_SIZEOF,
+ [PCI_EXT_CAP_ID_SRIOV] = PCI_EXT_CAP_SRIOV_SIZEOF,
+ [PCI_EXT_CAP_ID_MRIOV] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_MCAST] = PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF,
+ [PCI_EXT_CAP_ID_PRI] = PCI_EXT_CAP_PRI_SIZEOF,
+ [PCI_EXT_CAP_ID_AMD_XXX] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_REBAR] = 0xFF,
+ [PCI_EXT_CAP_ID_DPA] = 0xFF,
+ [PCI_EXT_CAP_ID_TPH] = 0xFF,
+ [PCI_EXT_CAP_ID_LTR] = PCI_EXT_CAP_LTR_SIZEOF,
+ [PCI_EXT_CAP_ID_SECPCI] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_PMUX] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_PASID] = 0, /* not yet */
+};
+
+/*
+ * Read/Write Permission Bits - one bit for each bit in capability
+ * Any field can be read if it exists, but what is read depends on
+ * whether the field is 'virtualized', or just pass thru to the
+ * hardware. Any virtualized field is also virtualized for writes.
+ * Writes are only permitted if they have a 1 bit here.
+ */
+struct perm_bits {
+ u8 *virt; /* read/write virtual data, not hw */
+ u8 *write; /* writeable bits */
+ int (*readfn)(struct vfio_pci_device *vdev, int pos, int count,
+ struct perm_bits *perm, int offset, __le32 *val);
+ int (*writefn)(struct vfio_pci_device *vdev, int pos, int count,
+ struct perm_bits *perm, int offset, __le32 val);
+};
+
+#define NO_VIRT 0
+#define ALL_VIRT 0xFFFFFFFFU
+#define NO_WRITE 0
+#define ALL_WRITE 0xFFFFFFFFU
+
+static int vfio_user_config_read(struct pci_dev *pdev, int offset,
+ __le32 *val, int count)
+{
+ int ret = -EINVAL;
+ u32 tmp_val = 0;
+
+ switch (count) {
+ case 1:
+ {
+ u8 tmp;
+ ret = pci_user_read_config_byte(pdev, offset, &tmp);
+ tmp_val = tmp;
+ break;
+ }
+ case 2:
+ {
+ u16 tmp;
+ ret = pci_user_read_config_word(pdev, offset, &tmp);
+ tmp_val = tmp;
+ break;
+ }
+ case 4:
+ ret = pci_user_read_config_dword(pdev, offset, &tmp_val);
+ break;
+ }
+
+ *val = cpu_to_le32(tmp_val);
+
+ return pcibios_err_to_errno(ret);
+}
+
+static int vfio_user_config_write(struct pci_dev *pdev, int offset,
+ __le32 val, int count)
+{
+ int ret = -EINVAL;
+ u32 tmp_val = le32_to_cpu(val);
+
+ switch (count) {
+ case 1:
+ ret = pci_user_write_config_byte(pdev, offset, tmp_val);
+ break;
+ case 2:
+ ret = pci_user_write_config_word(pdev, offset, tmp_val);
+ break;
+ case 4:
+ ret = pci_user_write_config_dword(pdev, offset, tmp_val);
+ break;
+ }
+
+ return pcibios_err_to_errno(ret);
+}
+
+static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ __le32 virt = 0;
+
+ memcpy(val, vdev->vconfig + pos, count);
+
+ memcpy(&virt, perm->virt + offset, count);
+
+ /* Any non-virtualized bits? */
+ if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) {
+ struct pci_dev *pdev = vdev->pdev;
+ __le32 phys_val = 0;
+ int ret;
+
+ ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+ if (ret)
+ return ret;
+
+ *val = (phys_val & ~virt) | (*val & virt);
+ }
+
+ return count;
+}
+
+static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ __le32 virt = 0, write = 0;
+
+ memcpy(&write, perm->write + offset, count);
+
+ if (!write)
+ return count; /* drop, no writable bits */
+
+ memcpy(&virt, perm->virt + offset, count);
+
+ /* Virtualized and writable bits go to vconfig */
+ if (write & virt) {
+ __le32 virt_val = 0;
+
+ memcpy(&virt_val, vdev->vconfig + pos, count);
+
+ virt_val &= ~(write & virt);
+ virt_val |= (val & (write & virt));
+
+ memcpy(vdev->vconfig + pos, &virt_val, count);
+ }
+
+ /* Non-virtualzed and writable bits go to hardware */
+ if (write & ~virt) {
+ struct pci_dev *pdev = vdev->pdev;
+ __le32 phys_val = 0;
+ int ret;
+
+ ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+ if (ret)
+ return ret;
+
+ phys_val &= ~(write & ~virt);
+ phys_val |= (val & (write & ~virt));
+
+ ret = vfio_user_config_write(pdev, pos, phys_val, count);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+
+/* Allow direct read from hardware, except for capability next pointer */
+static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ int ret;
+
+ ret = vfio_user_config_read(vdev->pdev, pos, val, count);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */
+ if (offset < 4)
+ memcpy(val, vdev->vconfig + pos, count);
+ } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */
+ if (offset == PCI_CAP_LIST_ID && count > 1)
+ memcpy(val, vdev->vconfig + pos,
+ min(PCI_CAP_FLAGS, count));
+ else if (offset == PCI_CAP_LIST_NEXT)
+ memcpy(val, vdev->vconfig + pos, 1);
+ }
+
+ return count;
+}
+
+static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ int ret;
+
+ ret = vfio_user_config_write(vdev->pdev, pos, val, count);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/* Default all regions to read-only, no-virtualization */
+static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = {
+ [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = {
+ [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+
+static void free_perm_bits(struct perm_bits *perm)
+{
+ kfree(perm->virt);
+ kfree(perm->write);
+ perm->virt = NULL;
+ perm->write = NULL;
+}
+
+static int alloc_perm_bits(struct perm_bits *perm, int size)
+{
+ /*
+ * Round up all permission bits to the next dword, this lets us
+ * ignore whether a read/write exceeds the defined capability
+ * structure. We can do this because:
+ * - Standard config space is already dword aligned
+ * - Capabilities are all dword alinged (bits 0:1 of next reserved)
+ * - Express capabilities defined as dword aligned
+ */
+ size = round_up(size, 4);
+
+ /*
+ * Zero state is
+ * - All Readable, None Writeable, None Virtualized
+ */
+ perm->virt = kzalloc(size, GFP_KERNEL);
+ perm->write = kzalloc(size, GFP_KERNEL);
+ if (!perm->virt || !perm->write) {
+ free_perm_bits(perm);
+ return -ENOMEM;
+ }
+
+ perm->readfn = vfio_default_config_read;
+ perm->writefn = vfio_default_config_write;
+
+ return 0;
+}
+
+/*
+ * Helper functions for filling in permission tables
+ */
+static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write)
+{
+ p->virt[off] = virt;
+ p->write[off] = write;
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write)
+{
+ *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt);
+ *(__le16 *)(&p->write[off]) = cpu_to_le16(write);
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write)
+{
+ *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt);
+ *(__le32 *)(&p->write[off]) = cpu_to_le32(write);
+}
+
+/*
+ * Restore the *real* BARs after we detect a FLR or backdoor reset.
+ * (backdoor = some device specific technique that we didn't catch)
+ */
+static void vfio_bar_restore(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u32 *rbar = vdev->rbar;
+ int i;
+
+ if (pdev->is_virtfn)
+ return;
+
+ pr_info("%s: %s reset recovery - restoring bars\n",
+ __func__, dev_name(&pdev->dev));
+
+ for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++)
+ pci_user_write_config_dword(pdev, i, *rbar);
+
+ pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar);
+}
+
+static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar)
+{
+ unsigned long flags = pci_resource_flags(pdev, bar);
+ u32 val;
+
+ if (flags & IORESOURCE_IO)
+ return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO);
+
+ val = PCI_BASE_ADDRESS_SPACE_MEMORY;
+
+ if (flags & IORESOURCE_PREFETCH)
+ val |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ if (flags & IORESOURCE_MEM_64)
+ val |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+ return cpu_to_le32(val);
+}
+
+/*
+ * Pretend we're hardware and tweak the values of the *virtual* PCI BARs
+ * to reflect the hardware capabilities. This implements BAR sizing.
+ */
+static void vfio_bar_fixup(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int i;
+ __le32 *bar;
+ u64 mask;
+
+ bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0];
+
+ for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) {
+ if (!pci_resource_start(pdev, i)) {
+ *bar = 0; /* Unmapped by host = unimplemented to user */
+ continue;
+ }
+
+ mask = ~(pci_resource_len(pdev, i) - 1);
+
+ *bar &= cpu_to_le32((u32)mask);
+ *bar |= vfio_generate_bar_flags(pdev, i);
+
+ if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+ bar++;
+ *bar &= cpu_to_le32((u32)(mask >> 32));
+ i++;
+ }
+ }
+
+ bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS];
+
+ /*
+ * NB. we expose the actual BAR size here, regardless of whether
+ * we can read it. When we report the REGION_INFO for the ROM
+ * we report what PCI tells us is the actual ROM size.
+ */
+ if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
+ mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
+ mask |= PCI_ROM_ADDRESS_ENABLE;
+ *bar &= cpu_to_le32((u32)mask);
+ } else
+ *bar = 0;
+
+ vdev->bardirty = false;
+}
+
+static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ if (is_bar(offset)) /* pos == offset for basic config */
+ vfio_bar_fixup(vdev);
+
+ count = vfio_default_config_read(vdev, pos, count, perm, offset, val);
+
+ /* Mask in virtual memory enable for SR-IOV devices */
+ if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) {
+ u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]);
+ u32 tmp_val = le32_to_cpu(*val);
+
+ tmp_val |= cmd & PCI_COMMAND_MEMORY;
+ *val = cpu_to_le32(tmp_val);
+ }
+
+ return count;
+}
+
+static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ __le16 *virt_cmd;
+ u16 new_cmd = 0;
+ int ret;
+
+ virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND];
+
+ if (offset == PCI_COMMAND) {
+ bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io;
+ u16 phys_cmd;
+
+ ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd);
+ if (ret)
+ return ret;
+
+ new_cmd = le32_to_cpu(val);
+
+ phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY);
+ virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY);
+ new_mem = !!(new_cmd & PCI_COMMAND_MEMORY);
+
+ phys_io = !!(phys_cmd & PCI_COMMAND_IO);
+ virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO);
+ new_io = !!(new_cmd & PCI_COMMAND_IO);
+
+ /*
+ * If the user is writing mem/io enable (new_mem/io) and we
+ * think it's already enabled (virt_mem/io), but the hardware
+ * shows it disabled (phys_mem/io, then the device has
+ * undergone some kind of backdoor reset and needs to be
+ * restored before we allow it to enable the bars.
+ * SR-IOV devices will trigger this, but we catch them later
+ */
+ if ((new_mem && virt_mem && !phys_mem) ||
+ (new_io && virt_io && !phys_io))
+ vfio_bar_restore(vdev);
+ }
+
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ if (count < 0)
+ return count;
+
+ /*
+ * Save current memory/io enable bits in vconfig to allow for
+ * the test above next time.
+ */
+ if (offset == PCI_COMMAND) {
+ u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+
+ *virt_cmd &= cpu_to_le16(~mask);
+ *virt_cmd |= cpu_to_le16(new_cmd & mask);
+ }
+
+ /* Emulate INTx disable */
+ if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) {
+ bool virt_intx_disable;
+
+ virt_intx_disable = !!(le16_to_cpu(*virt_cmd) &
+ PCI_COMMAND_INTX_DISABLE);
+
+ if (virt_intx_disable && !vdev->virq_disabled) {
+ vdev->virq_disabled = true;
+ vfio_pci_intx_mask(vdev);
+ } else if (!virt_intx_disable && vdev->virq_disabled) {
+ vdev->virq_disabled = false;
+ vfio_pci_intx_unmask(vdev);
+ }
+ }
+
+ if (is_bar(offset))
+ vdev->bardirty = true;
+
+ return count;
+}
+
+/* Permissions for the Basic PCI Header */
+static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF))
+ return -ENOMEM;
+
+ perm->readfn = vfio_basic_config_read;
+ perm->writefn = vfio_basic_config_write;
+
+ /* Virtualized for SR-IOV functions, which just have FFFF */
+ p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE);
+ p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Virtualize INTx disable, we use it internally for interrupt
+ * control and can emulate it for non-PCI 2.3 devices.
+ */
+ p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE);
+
+ /* Virtualize capability list, we might want to skip/disable */
+ p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE);
+
+ /* No harm to write */
+ p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE);
+ p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE);
+ p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE);
+
+ /* Virtualize all bars, can't touch the real ones */
+ p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE);
+
+ /* Allow us to adjust capability chain */
+ p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE);
+
+ /* Sometimes used by sw, just virtualize */
+ p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for the Power Management capability */
+static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM]))
+ return -ENOMEM;
+
+ /*
+ * We always virtualize the next field so we can remove
+ * capabilities from the chain if we want to.
+ */
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Power management is defined *per function*,
+ * so we let the user write this
+ */
+ p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for PCI-X capability */
+static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
+{
+ /* Alloc 24, but only 8 are used in v0 */
+ if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE);
+ p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for PCI Express capability */
+static int __init init_pci_cap_exp_perm(struct perm_bits *perm)
+{
+ /* Alloc larger of two possible sizes */
+ if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Allow writes to device control fields (includes FLR!)
+ * but not to devctl_phantom which could confuse IOMMU
+ * or to the ARI bit in devctl2 which is set at probe time
+ */
+ p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM);
+ p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI);
+ return 0;
+}
+
+/* Permissions for Advanced Function capability */
+static int __init init_pci_cap_af_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF]))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+ p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR);
+ return 0;
+}
+
+/* Permissions for Advanced Error Reporting extended capability */
+static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm)
+{
+ u32 mask;
+
+ if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR]))
+ return -ENOMEM;
+
+ /*
+ * Virtualize the first dword of all express capabilities
+ * because it includes the next pointer. This lets us later
+ * remove capabilities from the chain if we need to.
+ */
+ p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+ /* Writable bits mask */
+ mask = PCI_ERR_UNC_TRAIN | /* Training */
+ PCI_ERR_UNC_DLP | /* Data Link Protocol */
+ PCI_ERR_UNC_SURPDN | /* Surprise Down */
+ PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */
+ PCI_ERR_UNC_FCP | /* Flow Control Protocol */
+ PCI_ERR_UNC_COMP_TIME | /* Completion Timeout */
+ PCI_ERR_UNC_COMP_ABORT | /* Completer Abort */
+ PCI_ERR_UNC_UNX_COMP | /* Unexpected Completion */
+ PCI_ERR_UNC_RX_OVER | /* Receiver Overflow */
+ PCI_ERR_UNC_MALF_TLP | /* Malformed TLP */
+ PCI_ERR_UNC_ECRC | /* ECRC Error Status */
+ PCI_ERR_UNC_UNSUP | /* Unsupported Request */
+ PCI_ERR_UNC_ACSV | /* ACS Violation */
+ PCI_ERR_UNC_INTN | /* internal error */
+ PCI_ERR_UNC_MCBTLP | /* MC blocked TLP */
+ PCI_ERR_UNC_ATOMEG | /* Atomic egress blocked */
+ PCI_ERR_UNC_TLPPRE; /* TLP prefix blocked */
+ p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask);
+
+ mask = PCI_ERR_COR_RCVR | /* Receiver Error Status */
+ PCI_ERR_COR_BAD_TLP | /* Bad TLP Status */
+ PCI_ERR_COR_BAD_DLLP | /* Bad DLLP Status */
+ PCI_ERR_COR_REP_ROLL | /* REPLAY_NUM Rollover */
+ PCI_ERR_COR_REP_TIMER | /* Replay Timer Timeout */
+ PCI_ERR_COR_ADV_NFAT | /* Advisory Non-Fatal */
+ PCI_ERR_COR_INTERNAL | /* Corrected Internal */
+ PCI_ERR_COR_LOG_OVER; /* Header Log Overflow */
+ p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask);
+
+ mask = PCI_ERR_CAP_ECRC_GENE | /* ECRC Generation Enable */
+ PCI_ERR_CAP_ECRC_CHKE; /* ECRC Check Enable */
+ p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask);
+ return 0;
+}
+
+/* Permissions for Power Budgeting extended capability */
+static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR]))
+ return -ENOMEM;
+
+ p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+ /* Writing the data selector is OK, the info is still read-only */
+ p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE);
+ return 0;
+}
+
+/*
+ * Initialize the shared permission tables
+ */
+void vfio_pci_uninit_perm_bits(void)
+{
+ free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]);
+
+ free_perm_bits(&cap_perms[PCI_CAP_ID_PM]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_AF]);
+
+ free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+ free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+}
+
+int __init vfio_pci_init_perm_bits(void)
+{
+ int ret;
+
+ /* Basic config space */
+ ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]);
+
+ /* Capabilities */
+ ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]);
+ cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write;
+ ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]);
+ cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+ ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]);
+ ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]);
+
+ /* Extended capabilities */
+ ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+ ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+ ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+
+ if (ret)
+ vfio_pci_uninit_perm_bits();
+
+ return ret;
+}
+
+static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos)
+{
+ u8 cap;
+ int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
+ PCI_STD_HEADER_SIZEOF;
+ base /= 4;
+ pos /= 4;
+
+ cap = vdev->pci_config_map[pos];
+
+ if (cap == PCI_CAP_ID_BASIC)
+ return 0;
+
+ /* XXX Can we have to abutting capabilities of the same type? */
+ while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap)
+ pos--;
+
+ return pos * 4;
+}
+
+static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ /* Update max available queue size from msi_qmax */
+ if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+ __le16 *flags;
+ int start;
+
+ start = vfio_find_cap_start(vdev, pos);
+
+ flags = (__le16 *)&vdev->vconfig[start];
+
+ *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK);
+ *flags |= cpu_to_le16(vdev->msi_qmax << 1);
+ }
+
+ return vfio_default_config_read(vdev, pos, count, perm, offset, val);
+}
+
+static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ if (count < 0)
+ return count;
+
+ /* Fixup and write configured queue size and enable to hardware */
+ if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+ __le16 *pflags;
+ u16 flags;
+ int start, ret;
+
+ start = vfio_find_cap_start(vdev, pos);
+
+ pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS];
+
+ flags = le16_to_cpu(*pflags);
+
+ /* MSI is enabled via ioctl */
+ if (!is_msi(vdev))
+ flags &= ~PCI_MSI_FLAGS_ENABLE;
+
+ /* Check queue size */
+ if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) {
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ flags |= vdev->msi_qmax << 4;
+ }
+
+ /* Write back to virt and to hardware */
+ *pflags = cpu_to_le16(flags);
+ ret = pci_user_write_config_word(vdev->pdev,
+ start + PCI_MSI_FLAGS,
+ flags);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+ }
+
+ return count;
+}
+
+/*
+ * MSI determination is per-device, so this routine gets used beyond
+ * initialization time. Don't add __init
+ */
+static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags)
+{
+ if (alloc_perm_bits(perm, len))
+ return -ENOMEM;
+
+ perm->readfn = vfio_msi_config_read;
+ perm->writefn = vfio_msi_config_write;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * The upper byte of the control register is reserved,
+ * just setup the lower byte.
+ */
+ p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE);
+ p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_64BIT) {
+ p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE);
+ p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE);
+ }
+ } else {
+ p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE);
+ }
+ }
+ return 0;
+}
+
+/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */
+static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int len, ret;
+ u16 flags;
+
+ ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ len = 10; /* Minimum size */
+ if (flags & PCI_MSI_FLAGS_64BIT)
+ len += 4;
+ if (flags & PCI_MSI_FLAGS_MASKBIT)
+ len += 10;
+
+ if (vdev->msi_perm)
+ return len;
+
+ vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL);
+ if (!vdev->msi_perm)
+ return -ENOMEM;
+
+ ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+/* Determine extended capability length for VC (2 & 9) and MFVC */
+static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u32 tmp;
+ int ret, evcc, phases, vc_arb;
+ int len = PCI_CAP_VC_BASE_SIZEOF;
+
+ ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */
+ ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (tmp & PCI_VC_REG2_128_PHASE)
+ phases = 128;
+ else if (tmp & PCI_VC_REG2_64_PHASE)
+ phases = 64;
+ else if (tmp & PCI_VC_REG2_32_PHASE)
+ phases = 32;
+ else
+ phases = 0;
+
+ vc_arb = phases * 4;
+
+ /*
+ * Port arbitration tables are root & switch only;
+ * function arbitration tables are function 0 only.
+ * In either case, we'll never let user write them so
+ * we don't care how big they are
+ */
+ len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF;
+ if (vc_arb) {
+ len = round_up(len, 16);
+ len += vc_arb / 8;
+ }
+ return len;
+}
+
+static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u16 word;
+ u8 byte;
+ int ret;
+
+ switch (cap) {
+ case PCI_CAP_ID_MSI:
+ return vfio_msi_cap_len(vdev, pos);
+ case PCI_CAP_ID_PCIX:
+ ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (PCI_X_CMD_VERSION(word)) {
+ vdev->extended_caps = true;
+ return PCI_CAP_PCIX_SIZEOF_V2;
+ } else
+ return PCI_CAP_PCIX_SIZEOF_V0;
+ case PCI_CAP_ID_VNDR:
+ /* length follows next field */
+ ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return byte;
+ case PCI_CAP_ID_EXP:
+ /* length based on version */
+ ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if ((word & PCI_EXP_FLAGS_VERS) == 1)
+ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1;
+ else {
+ vdev->extended_caps = true;
+ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2;
+ }
+ case PCI_CAP_ID_HT:
+ ret = pci_read_config_byte(pdev, pos + 3, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return (byte & HT_3BIT_CAP_MASK) ?
+ HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG;
+ case PCI_CAP_ID_SATA:
+ ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_SATA_REGS_MASK;
+ if (byte == PCI_SATA_REGS_INLINE)
+ return PCI_SATA_SIZEOF_LONG;
+ else
+ return PCI_SATA_SIZEOF_SHORT;
+ default:
+ pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n",
+ dev_name(&pdev->dev), __func__, cap, pos);
+ }
+
+ return 0;
+}
+
+static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 byte;
+ u32 dword;
+ int ret;
+
+ switch (ecap) {
+ case PCI_EXT_CAP_ID_VNDR:
+ ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return dword >> PCI_VSEC_HDR_LEN_SHIFT;
+ case PCI_EXT_CAP_ID_VC:
+ case PCI_EXT_CAP_ID_VC9:
+ case PCI_EXT_CAP_ID_MFVC:
+ return vfio_vc_cap_len(vdev, epos);
+ case PCI_EXT_CAP_ID_ACS:
+ ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (byte & PCI_ACS_EC) {
+ int bits;
+
+ ret = pci_read_config_byte(pdev,
+ epos + PCI_ACS_EGRESS_BITS,
+ &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ bits = byte ? round_up(byte, 32) : 256;
+ return 8 + (bits / 8);
+ }
+ return 8;
+
+ case PCI_EXT_CAP_ID_REBAR:
+ ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_REBAR_CTRL_NBAR_MASK;
+ byte >>= PCI_REBAR_CTRL_NBAR_SHIFT;
+
+ return 4 + (byte * 8);
+ case PCI_EXT_CAP_ID_DPA:
+ ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_DPA_CAP_SUBSTATE_MASK;
+ byte = round_up(byte + 1, 4);
+ return PCI_DPA_BASE_SIZEOF + byte;
+ case PCI_EXT_CAP_ID_TPH:
+ ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) {
+ int sts;
+
+ sts = byte & PCI_TPH_CAP_ST_MASK;
+ sts >>= PCI_TPH_CAP_ST_SHIFT;
+ return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4);
+ }
+ return PCI_TPH_BASE_SIZEOF;
+ default:
+ pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n",
+ dev_name(&pdev->dev), __func__, ecap, epos);
+ }
+
+ return 0;
+}
+
+static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev,
+ int offset, int size)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret = 0;
+
+ /*
+ * We try to read physical config space in the largest chunks
+ * we can, assuming that all of the fields support dword access.
+ * pci_save_state() makes this same assumption and seems to do ok.
+ */
+ while (size) {
+ int filled;
+
+ if (size >= 4 && !(offset % 4)) {
+ __le32 *dwordp = (__le32 *)&vdev->vconfig[offset];
+ u32 dword;
+
+ ret = pci_read_config_dword(pdev, offset, &dword);
+ if (ret)
+ return ret;
+ *dwordp = cpu_to_le32(dword);
+ filled = 4;
+ } else if (size >= 2 && !(offset % 2)) {
+ __le16 *wordp = (__le16 *)&vdev->vconfig[offset];
+ u16 word;
+
+ ret = pci_read_config_word(pdev, offset, &word);
+ if (ret)
+ return ret;
+ *wordp = cpu_to_le16(word);
+ filled = 2;
+ } else {
+ u8 *byte = &vdev->vconfig[offset];
+ ret = pci_read_config_byte(pdev, offset, byte);
+ if (ret)
+ return ret;
+ filled = 1;
+ }
+
+ offset += filled;
+ size -= filled;
+ }
+
+ return ret;
+}
+
+static int vfio_cap_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map = vdev->pci_config_map;
+ u16 status;
+ u8 pos, *prev, cap;
+ int loops, ret, caps = 0;
+
+ /* Any capabilities? */
+ ret = pci_read_config_word(pdev, PCI_STATUS, &status);
+ if (ret)
+ return ret;
+
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0; /* Done */
+
+ ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos);
+ if (ret)
+ return ret;
+
+ /* Mark the previous position in case we want to skip a capability */
+ prev = &vdev->vconfig[PCI_CAPABILITY_LIST];
+
+ /* We can bound our loop, capabilities are dword aligned */
+ loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF;
+ while (pos && loops--) {
+ u8 next;
+ int i, len = 0;
+
+ ret = pci_read_config_byte(pdev, pos, &cap);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_byte(pdev,
+ pos + PCI_CAP_LIST_NEXT, &next);
+ if (ret)
+ return ret;
+
+ if (cap <= PCI_CAP_ID_MAX) {
+ len = pci_cap_length[cap];
+ if (len == 0xFF) { /* Variable length */
+ len = vfio_cap_len(vdev, cap, pos);
+ if (len < 0)
+ return len;
+ }
+ }
+
+ if (!len) {
+ pr_info("%s: %s hiding cap 0x%x\n",
+ __func__, dev_name(&pdev->dev), cap);
+ *prev = next;
+ pos = next;
+ continue;
+ }
+
+ /* Sanity check, do we overlap other capabilities? */
+ for (i = 0; i < len; i += 4) {
+ if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID))
+ continue;
+
+ pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n",
+ __func__, dev_name(&pdev->dev),
+ pos + i, map[pos + i], cap);
+ }
+
+ memset(map + (pos / 4), cap, len / 4);
+ ret = vfio_fill_vconfig_bytes(vdev, pos, len);
+ if (ret)
+ return ret;
+
+ prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT];
+ pos = next;
+ caps++;
+ }
+
+ /* If we didn't fill any capabilities, clear the status flag */
+ if (!caps) {
+ __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS];
+ *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST);
+ }
+
+ return 0;
+}
+
+static int vfio_ecap_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map = vdev->pci_config_map;
+ u16 epos;
+ __le32 *prev = NULL;
+ int loops, ret, ecaps = 0;
+
+ if (!vdev->extended_caps)
+ return 0;
+
+ epos = PCI_CFG_SPACE_SIZE;
+
+ loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF;
+
+ while (loops-- && epos >= PCI_CFG_SPACE_SIZE) {
+ u32 header;
+ u16 ecap;
+ int i, len = 0;
+ bool hidden = false;
+
+ ret = pci_read_config_dword(pdev, epos, &header);
+ if (ret)
+ return ret;
+
+ ecap = PCI_EXT_CAP_ID(header);
+
+ if (ecap <= PCI_EXT_CAP_ID_MAX) {
+ len = pci_ext_cap_length[ecap];
+ if (len == 0xFF) {
+ len = vfio_ext_cap_len(vdev, ecap, epos);
+ if (len < 0)
+ return ret;
+ }
+ }
+
+ if (!len) {
+ pr_info("%s: %s hiding ecap 0x%x@0x%x\n",
+ __func__, dev_name(&pdev->dev), ecap, epos);
+
+ /* If not the first in the chain, we can skip over it */
+ if (prev) {
+ u32 val = epos = PCI_EXT_CAP_NEXT(header);
+ *prev &= cpu_to_le32(~(0xffcU << 20));
+ *prev |= cpu_to_le32(val << 20);
+ continue;
+ }
+
+ /*
+ * Otherwise, fill in a placeholder, the direct
+ * readfn will virtualize this automatically
+ */
+ len = PCI_CAP_SIZEOF;
+ hidden = true;
+ }
+
+ for (i = 0; i < len; i += 4) {
+ if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID))
+ continue;
+
+ pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n",
+ __func__, dev_name(&pdev->dev),
+ epos + i, map[epos + i], ecap);
+ }
+
+ /*
+ * Even though ecap is 2 bytes, we're currently a long way
+ * from exceeding 1 byte capabilities. If we ever make it
+ * up to 0xFF we'll need to up this to a two-byte, byte map.
+ */
+ BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID);
+
+ memset(map + (epos / 4), ecap, len / 4);
+ ret = vfio_fill_vconfig_bytes(vdev, epos, len);
+ if (ret)
+ return ret;
+
+ /*
+ * If we're just using this capability to anchor the list,
+ * hide the real ID. Only count real ecaps. XXX PCI spec
+ * indicates to use cap id = 0, version = 0, next = 0 if
+ * ecaps are absent, hope users check all the way to next.
+ */
+ if (hidden)
+ *(__le32 *)&vdev->vconfig[epos] &=
+ cpu_to_le32((0xffcU << 20));
+ else
+ ecaps++;
+
+ prev = (__le32 *)&vdev->vconfig[epos];
+ epos = PCI_EXT_CAP_NEXT(header);
+ }
+
+ if (!ecaps)
+ *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0;
+
+ return 0;
+}
+
+/*
+ * For each device we allocate a pci_config_map that indicates the
+ * capability occupying each dword and thus the struct perm_bits we
+ * use for read and write. We also allocate a virtualized config
+ * space which tracks reads and writes to bits that we emulate for
+ * the user. Initial values filled from device.
+ *
+ * Using shared stuct perm_bits between all vfio-pci devices saves
+ * us from allocating cfg_size buffers for virt and write for every
+ * device. We could remove vconfig and allocate individual buffers
+ * for each area requring emulated bits, but the array of pointers
+ * would be comparable in size (at least for standard config space).
+ */
+int vfio_config_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map, *vconfig;
+ int ret;
+
+ /*
+ * Config space, caps and ecaps are all dword aligned, so we can
+ * use one byte per dword to record the type.
+ */
+ map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL);
+ if (!vconfig) {
+ kfree(map);
+ return -ENOMEM;
+ }
+
+ vdev->pci_config_map = map;
+ vdev->vconfig = vconfig;
+
+ memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4);
+ memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID,
+ (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4);
+
+ ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF);
+ if (ret)
+ goto out;
+
+ vdev->bardirty = true;
+
+ /*
+ * XXX can we just pci_load_saved_state/pci_restore_state?
+ * may need to rebuild vconfig after that
+ */
+
+ /* For restore after reset */
+ vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]);
+ vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]);
+ vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]);
+ vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]);
+ vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]);
+ vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]);
+ vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]);
+
+ if (pdev->is_virtfn) {
+ *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor);
+ *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
+ }
+
+ ret = vfio_cap_init(vdev);
+ if (ret)
+ goto out;
+
+ ret = vfio_ecap_init(vdev);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ kfree(map);
+ vdev->pci_config_map = NULL;
+ kfree(vconfig);
+ vdev->vconfig = NULL;
+ return pcibios_err_to_errno(ret);
+}
+
+void vfio_config_free(struct vfio_pci_device *vdev)
+{
+ kfree(vdev->vconfig);
+ vdev->vconfig = NULL;
+ kfree(vdev->pci_config_map);
+ vdev->pci_config_map = NULL;
+ kfree(vdev->msi_perm);
+ vdev->msi_perm = NULL;
+}
+
+static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ struct perm_bits *perm;
+ __le32 val = 0;
+ int cap_start = 0, offset;
+ u8 cap_id;
+ ssize_t ret = count;
+
+ if (*ppos < 0 || *ppos + count > pdev->cfg_size)
+ return -EFAULT;
+
+ /*
+ * gcc can't seem to figure out we're a static function, only called
+ * with count of 1/2/4 and hits copy_from_user_overflow without this.
+ */
+ if (count > sizeof(val))
+ return -EINVAL;
+
+ cap_id = vdev->pci_config_map[*ppos / 4];
+
+ if (cap_id == PCI_CAP_ID_INVALID) {
+ if (iswrite)
+ return ret; /* drop */
+
+ /*
+ * Per PCI spec 3.0, section 6.1, reads from reserved and
+ * unimplemented registers return 0
+ */
+ if (copy_to_user(buf, &val, count))
+ return -EFAULT;
+
+ return ret;
+ }
+
+ /*
+ * All capabilities are minimum 4 bytes and aligned on dword
+ * boundaries. Since we don't support unaligned accesses, we're
+ * only ever accessing a single capability.
+ */
+ if (*ppos >= PCI_CFG_SPACE_SIZE) {
+ WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX);
+
+ perm = &ecap_perms[cap_id];
+ cap_start = vfio_find_cap_start(vdev, *ppos);
+
+ } else {
+ WARN_ON(cap_id > PCI_CAP_ID_MAX);
+
+ perm = &cap_perms[cap_id];
+
+ if (cap_id == PCI_CAP_ID_MSI)
+ perm = vdev->msi_perm;
+
+ if (cap_id > PCI_CAP_ID_BASIC)
+ cap_start = vfio_find_cap_start(vdev, *ppos);
+ }
+
+ WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC);
+ WARN_ON(cap_start > *ppos);
+
+ offset = *ppos - cap_start;
+
+ if (iswrite) {
+ if (!perm->writefn)
+ return ret;
+
+ if (copy_from_user(&val, buf, count))
+ return -EFAULT;
+
+ ret = perm->writefn(vdev, *ppos, count, perm, offset, val);
+ } else {
+ if (perm->readfn) {
+ ret = perm->readfn(vdev, *ppos, count,
+ perm, offset, &val);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (copy_to_user(buf, &val, count))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite)
+{
+ size_t done = 0;
+ int ret = 0;
+ loff_t pos = *ppos;
+
+ pos &= VFIO_PCI_OFFSET_MASK;
+
+ /*
+ * We want to both keep the access size the caller users as well as
+ * support reading large chunks of config space in a single call.
+ * PCI doesn't support unaligned accesses, so we can safely break
+ * those apart.
+ */
+ while (count) {
+ if (count >= 4 && !(pos % 4))
+ ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite);
+ else if (count >= 2 && !(pos % 2))
+ ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite);
+ else
+ ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite);
+
+ if (ret < 0)
+ return ret;
+
+ count -= ret;
+ done += ret;
+ buf += ret;
+ pos += ret;
+ }
+
+ *ppos += done;
+
+ return done;
+}
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
new file mode 100644
index 000000000000..211a4920b88a
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -0,0 +1,740 @@
+/*
+ * VFIO PCI interrupt handling
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/eventfd.h>
+#include <linux/pci.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include "vfio_pci_private.h"
+
+/*
+ * IRQfd - generic
+ */
+struct virqfd {
+ struct vfio_pci_device *vdev;
+ struct eventfd_ctx *eventfd;
+ int (*handler)(struct vfio_pci_device *, void *);
+ void (*thread)(struct vfio_pci_device *, void *);
+ void *data;
+ struct work_struct inject;
+ wait_queue_t wait;
+ poll_table pt;
+ struct work_struct shutdown;
+ struct virqfd **pvirqfd;
+};
+
+static struct workqueue_struct *vfio_irqfd_cleanup_wq;
+
+int __init vfio_pci_virqfd_init(void)
+{
+ vfio_irqfd_cleanup_wq =
+ create_singlethread_workqueue("vfio-irqfd-cleanup");
+ if (!vfio_irqfd_cleanup_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void vfio_pci_virqfd_exit(void)
+{
+ destroy_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+static void virqfd_deactivate(struct virqfd *virqfd)
+{
+ queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown);
+}
+
+static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ struct virqfd *virqfd = container_of(wait, struct virqfd, wait);
+ unsigned long flags = (unsigned long)key;
+
+ if (flags & POLLIN) {
+ /* An event has been signaled, call function */
+ if ((!virqfd->handler ||
+ virqfd->handler(virqfd->vdev, virqfd->data)) &&
+ virqfd->thread)
+ schedule_work(&virqfd->inject);
+ }
+
+ if (flags & POLLHUP)
+ /* The eventfd is closing, detach from VFIO */
+ virqfd_deactivate(virqfd);
+
+ return 0;
+}
+
+static void virqfd_ptable_queue_proc(struct file *file,
+ wait_queue_head_t *wqh, poll_table *pt)
+{
+ struct virqfd *virqfd = container_of(pt, struct virqfd, pt);
+ add_wait_queue(wqh, &virqfd->wait);
+}
+
+static void virqfd_shutdown(struct work_struct *work)
+{
+ struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
+ struct virqfd **pvirqfd = virqfd->pvirqfd;
+ u64 cnt;
+
+ eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
+ flush_work(&virqfd->inject);
+ eventfd_ctx_put(virqfd->eventfd);
+
+ kfree(virqfd);
+ *pvirqfd = NULL;
+}
+
+static void virqfd_inject(struct work_struct *work)
+{
+ struct virqfd *virqfd = container_of(work, struct virqfd, inject);
+ if (virqfd->thread)
+ virqfd->thread(virqfd->vdev, virqfd->data);
+}
+
+static int virqfd_enable(struct vfio_pci_device *vdev,
+ int (*handler)(struct vfio_pci_device *, void *),
+ void (*thread)(struct vfio_pci_device *, void *),
+ void *data, struct virqfd **pvirqfd, int fd)
+{
+ struct file *file = NULL;
+ struct eventfd_ctx *ctx = NULL;
+ struct virqfd *virqfd;
+ int ret = 0;
+ unsigned int events;
+
+ if (*pvirqfd)
+ return -EBUSY;
+
+ virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
+ if (!virqfd)
+ return -ENOMEM;
+
+ virqfd->pvirqfd = pvirqfd;
+ *pvirqfd = virqfd;
+ virqfd->vdev = vdev;
+ virqfd->handler = handler;
+ virqfd->thread = thread;
+ virqfd->data = data;
+
+ INIT_WORK(&virqfd->shutdown, virqfd_shutdown);
+ INIT_WORK(&virqfd->inject, virqfd_inject);
+
+ file = eventfd_fget(fd);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto fail;
+ }
+
+ ctx = eventfd_ctx_fileget(file);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto fail;
+ }
+
+ virqfd->eventfd = ctx;
+
+ /*
+ * Install our own custom wake-up handling so we are notified via
+ * a callback whenever someone signals the underlying eventfd.
+ */
+ init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup);
+ init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc);
+
+ events = file->f_op->poll(file, &virqfd->pt);
+
+ /*
+ * Check if there was an event already pending on the eventfd
+ * before we registered and trigger it as if we didn't miss it.
+ */
+ if (events & POLLIN) {
+ if ((!handler || handler(vdev, data)) && thread)
+ schedule_work(&virqfd->inject);
+ }
+
+ /*
+ * Do not drop the file until the irqfd is fully initialized,
+ * otherwise we might race against the POLLHUP.
+ */
+ fput(file);
+
+ return 0;
+
+fail:
+ if (ctx && !IS_ERR(ctx))
+ eventfd_ctx_put(ctx);
+
+ if (file && !IS_ERR(file))
+ fput(file);
+
+ kfree(virqfd);
+ *pvirqfd = NULL;
+
+ return ret;
+}
+
+static void virqfd_disable(struct virqfd *virqfd)
+{
+ if (!virqfd)
+ return;
+
+ virqfd_deactivate(virqfd);
+
+ /* Block until we know all outstanding shutdown jobs have completed. */
+ flush_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+/*
+ * INTx
+ */
+static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused)
+{
+ if (likely(is_intx(vdev) && !vdev->virq_disabled))
+ eventfd_signal(vdev->ctx[0].trigger, 1);
+}
+
+void vfio_pci_intx_mask(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ /*
+ * Masking can come from interrupt, ioctl, or config space
+ * via INTx disable. The latter means this can get called
+ * even when not using intx delivery. In this case, just
+ * try to have the physical bit follow the virtual bit.
+ */
+ if (unlikely(!is_intx(vdev))) {
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 0);
+ } else if (!vdev->ctx[0].masked) {
+ /*
+ * Can't use check_and_mask here because we always want to
+ * mask, not just when something is pending.
+ */
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 0);
+ else
+ disable_irq_nosync(pdev->irq);
+
+ vdev->ctx[0].masked = true;
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+}
+
+/*
+ * If this is triggered by an eventfd, we can't call eventfd_signal
+ * or else we'll deadlock on the eventfd wait queue. Return >0 when
+ * a signal is necessary, which can then be handled via a work queue
+ * or directly depending on the caller.
+ */
+int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ /*
+ * Unmasking comes from ioctl or config, so again, have the
+ * physical bit follow the virtual even when not using INTx.
+ */
+ if (unlikely(!is_intx(vdev))) {
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 1);
+ } else if (vdev->ctx[0].masked && !vdev->virq_disabled) {
+ /*
+ * A pending interrupt here would immediately trigger,
+ * but we can avoid that overhead by just re-sending
+ * the interrupt to the user.
+ */
+ if (vdev->pci_2_3) {
+ if (!pci_check_and_unmask_intx(pdev))
+ ret = 1;
+ } else
+ enable_irq(pdev->irq);
+
+ vdev->ctx[0].masked = (ret > 0);
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ return ret;
+}
+
+void vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
+{
+ if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0)
+ vfio_send_intx_eventfd(vdev, NULL);
+}
+
+static irqreturn_t vfio_intx_handler(int irq, void *dev_id)
+{
+ struct vfio_pci_device *vdev = dev_id;
+ unsigned long flags;
+ int ret = IRQ_NONE;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ if (!vdev->pci_2_3) {
+ disable_irq_nosync(vdev->pdev->irq);
+ vdev->ctx[0].masked = true;
+ ret = IRQ_HANDLED;
+ } else if (!vdev->ctx[0].masked && /* may be shared */
+ pci_check_and_mask_intx(vdev->pdev)) {
+ vdev->ctx[0].masked = true;
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ if (ret == IRQ_HANDLED)
+ vfio_send_intx_eventfd(vdev, NULL);
+
+ return ret;
+}
+
+static int vfio_intx_enable(struct vfio_pci_device *vdev)
+{
+ if (!is_irq_none(vdev))
+ return -EINVAL;
+
+ if (!vdev->pdev->irq)
+ return -ENODEV;
+
+ vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ if (!vdev->ctx)
+ return -ENOMEM;
+
+ vdev->num_ctx = 1;
+ vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX;
+
+ return 0;
+}
+
+static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long irqflags = IRQF_SHARED;
+ struct eventfd_ctx *trigger;
+ unsigned long flags;
+ int ret;
+
+ if (vdev->ctx[0].trigger) {
+ free_irq(pdev->irq, vdev);
+ kfree(vdev->ctx[0].name);
+ eventfd_ctx_put(vdev->ctx[0].trigger);
+ vdev->ctx[0].trigger = NULL;
+ }
+
+ if (fd < 0) /* Disable only */
+ return 0;
+
+ vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)",
+ pci_name(pdev));
+ if (!vdev->ctx[0].name)
+ return -ENOMEM;
+
+ trigger = eventfd_ctx_fdget(fd);
+ if (IS_ERR(trigger)) {
+ kfree(vdev->ctx[0].name);
+ return PTR_ERR(trigger);
+ }
+
+ if (!vdev->pci_2_3)
+ irqflags = 0;
+
+ ret = request_irq(pdev->irq, vfio_intx_handler,
+ irqflags, vdev->ctx[0].name, vdev);
+ if (ret) {
+ kfree(vdev->ctx[0].name);
+ eventfd_ctx_put(trigger);
+ return ret;
+ }
+
+ vdev->ctx[0].trigger = trigger;
+
+ /*
+ * INTx disable will stick across the new irq setup,
+ * disable_irq won't.
+ */
+ spin_lock_irqsave(&vdev->irqlock, flags);
+ if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled))
+ disable_irq_nosync(pdev->irq);
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ return 0;
+}
+
+static void vfio_intx_disable(struct vfio_pci_device *vdev)
+{
+ vfio_intx_set_signal(vdev, -1);
+ virqfd_disable(vdev->ctx[0].unmask);
+ virqfd_disable(vdev->ctx[0].mask);
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ vdev->num_ctx = 0;
+ kfree(vdev->ctx);
+}
+
+/*
+ * MSI/MSI-X
+ */
+static irqreturn_t vfio_msihandler(int irq, void *arg)
+{
+ struct eventfd_ctx *trigger = arg;
+
+ eventfd_signal(trigger, 1);
+ return IRQ_HANDLED;
+}
+
+static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+
+ if (!is_irq_none(vdev))
+ return -EINVAL;
+
+ vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ if (!vdev->ctx)
+ return -ENOMEM;
+
+ if (msix) {
+ int i;
+
+ vdev->msix = kzalloc(nvec * sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!vdev->msix) {
+ kfree(vdev->ctx);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < nvec; i++)
+ vdev->msix[i].entry = i;
+
+ ret = pci_enable_msix(pdev, vdev->msix, nvec);
+ if (ret) {
+ kfree(vdev->msix);
+ kfree(vdev->ctx);
+ return ret;
+ }
+ } else {
+ ret = pci_enable_msi_block(pdev, nvec);
+ if (ret) {
+ kfree(vdev->ctx);
+ return ret;
+ }
+ }
+
+ vdev->num_ctx = nvec;
+ vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX :
+ VFIO_PCI_MSI_IRQ_INDEX;
+
+ if (!msix) {
+ /*
+ * Compute the virtual hardware field for max msi vectors -
+ * it is the log base 2 of the number of vectors.
+ */
+ vdev->msi_qmax = fls(nvec * 2 - 1) - 1;
+ }
+
+ return 0;
+}
+
+static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
+ int vector, int fd, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector;
+ char *name = msix ? "vfio-msix" : "vfio-msi";
+ struct eventfd_ctx *trigger;
+ int ret;
+
+ if (vector >= vdev->num_ctx)
+ return -EINVAL;
+
+ if (vdev->ctx[vector].trigger) {
+ free_irq(irq, vdev->ctx[vector].trigger);
+ kfree(vdev->ctx[vector].name);
+ eventfd_ctx_put(vdev->ctx[vector].trigger);
+ vdev->ctx[vector].trigger = NULL;
+ }
+
+ if (fd < 0)
+ return 0;
+
+ vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)",
+ name, vector, pci_name(pdev));
+ if (!vdev->ctx[vector].name)
+ return -ENOMEM;
+
+ trigger = eventfd_ctx_fdget(fd);
+ if (IS_ERR(trigger)) {
+ kfree(vdev->ctx[vector].name);
+ return PTR_ERR(trigger);
+ }
+
+ ret = request_irq(irq, vfio_msihandler, 0,
+ vdev->ctx[vector].name, trigger);
+ if (ret) {
+ kfree(vdev->ctx[vector].name);
+ eventfd_ctx_put(trigger);
+ return ret;
+ }
+
+ vdev->ctx[vector].trigger = trigger;
+
+ return 0;
+}
+
+static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
+ unsigned count, int32_t *fds, bool msix)
+{
+ int i, j, ret = 0;
+
+ if (start + count > vdev->num_ctx)
+ return -EINVAL;
+
+ for (i = 0, j = start; i < count && !ret; i++, j++) {
+ int fd = fds ? fds[i] : -1;
+ ret = vfio_msi_set_vector_signal(vdev, j, fd, msix);
+ }
+
+ if (ret) {
+ for (--j; j >= start; j--)
+ vfio_msi_set_vector_signal(vdev, j, -1, msix);
+ }
+
+ return ret;
+}
+
+static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int i;
+
+ vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
+
+ for (i = 0; i < vdev->num_ctx; i++) {
+ virqfd_disable(vdev->ctx[i].unmask);
+ virqfd_disable(vdev->ctx[i].mask);
+ }
+
+ if (msix) {
+ pci_disable_msix(vdev->pdev);
+ kfree(vdev->msix);
+ } else
+ pci_disable_msi(pdev);
+
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ vdev->num_ctx = 0;
+ kfree(vdev->ctx);
+}
+
+/*
+ * IOCTL support
+ */
+static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (!is_intx(vdev) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_pci_intx_unmask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t unmask = *(uint8_t *)data;
+ if (unmask)
+ vfio_pci_intx_unmask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t fd = *(int32_t *)data;
+ if (fd >= 0)
+ return virqfd_enable(vdev, vfio_pci_intx_unmask_handler,
+ vfio_send_intx_eventfd, NULL,
+ &vdev->ctx[0].unmask, fd);
+
+ virqfd_disable(vdev->ctx[0].unmask);
+ }
+
+ return 0;
+}
+
+static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (!is_intx(vdev) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_pci_intx_mask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t mask = *(uint8_t *)data;
+ if (mask)
+ vfio_pci_intx_mask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ return -ENOTTY; /* XXX implement me */
+ }
+
+ return 0;
+}
+
+static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+ vfio_intx_disable(vdev);
+ return 0;
+ }
+
+ if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t fd = *(int32_t *)data;
+ int ret;
+
+ if (is_intx(vdev))
+ return vfio_intx_set_signal(vdev, fd);
+
+ ret = vfio_intx_enable(vdev);
+ if (ret)
+ return ret;
+
+ ret = vfio_intx_set_signal(vdev, fd);
+ if (ret)
+ vfio_intx_disable(vdev);
+
+ return ret;
+ }
+
+ if (!is_intx(vdev))
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_send_intx_eventfd(vdev, NULL);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t trigger = *(uint8_t *)data;
+ if (trigger)
+ vfio_send_intx_eventfd(vdev, NULL);
+ }
+ return 0;
+}
+
+static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ int i;
+ bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false;
+
+ if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+ vfio_msi_disable(vdev, msix);
+ return 0;
+ }
+
+ if (!(irq_is(vdev, index) || is_irq_none(vdev)))
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t *fds = data;
+ int ret;
+
+ if (vdev->irq_type == index)
+ return vfio_msi_set_block(vdev, start, count,
+ fds, msix);
+
+ ret = vfio_msi_enable(vdev, start + count, msix);
+ if (ret)
+ return ret;
+
+ ret = vfio_msi_set_block(vdev, start, count, fds, msix);
+ if (ret)
+ vfio_msi_disable(vdev, msix);
+
+ return ret;
+ }
+
+ if (!irq_is(vdev, index) || start + count > vdev->num_ctx)
+ return -EINVAL;
+
+ for (i = start; i < start + count; i++) {
+ if (!vdev->ctx[i].trigger)
+ continue;
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ eventfd_signal(vdev->ctx[i].trigger, 1);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t *bools = data;
+ if (bools[i - start])
+ eventfd_signal(vdev->ctx[i].trigger, 1);
+ }
+ }
+ return 0;
+}
+
+int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
+ unsigned index, unsigned start, unsigned count,
+ void *data)
+{
+ int (*func)(struct vfio_pci_device *vdev, unsigned index,
+ unsigned start, unsigned count, uint32_t flags,
+ void *data) = NULL;
+
+ switch (index) {
+ case VFIO_PCI_INTX_IRQ_INDEX:
+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+ case VFIO_IRQ_SET_ACTION_MASK:
+ func = vfio_pci_set_intx_mask;
+ break;
+ case VFIO_IRQ_SET_ACTION_UNMASK:
+ func = vfio_pci_set_intx_unmask;
+ break;
+ case VFIO_IRQ_SET_ACTION_TRIGGER:
+ func = vfio_pci_set_intx_trigger;
+ break;
+ }
+ break;
+ case VFIO_PCI_MSI_IRQ_INDEX:
+ case VFIO_PCI_MSIX_IRQ_INDEX:
+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+ case VFIO_IRQ_SET_ACTION_MASK:
+ case VFIO_IRQ_SET_ACTION_UNMASK:
+ /* XXX Need masking support exported */
+ break;
+ case VFIO_IRQ_SET_ACTION_TRIGGER:
+ func = vfio_pci_set_msi_trigger;
+ break;
+ }
+ break;
+ }
+
+ if (!func)
+ return -ENOTTY;
+
+ return func(vdev, index, start, count, flags, data);
+}
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
new file mode 100644
index 000000000000..611827cba8cd
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/mutex.h>
+#include <linux/pci.h>
+
+#ifndef VFIO_PCI_PRIVATE_H
+#define VFIO_PCI_PRIVATE_H
+
+#define VFIO_PCI_OFFSET_SHIFT 40
+
+#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
+
+struct vfio_pci_irq_ctx {
+ struct eventfd_ctx *trigger;
+ struct virqfd *unmask;
+ struct virqfd *mask;
+ char *name;
+ bool masked;
+};
+
+struct vfio_pci_device {
+ struct pci_dev *pdev;
+ void __iomem *barmap[PCI_STD_RESOURCE_END + 1];
+ u8 *pci_config_map;
+ u8 *vconfig;
+ struct perm_bits *msi_perm;
+ spinlock_t irqlock;
+ struct mutex igate;
+ struct msix_entry *msix;
+ struct vfio_pci_irq_ctx *ctx;
+ int num_ctx;
+ int irq_type;
+ u8 msi_qmax;
+ u8 msix_bar;
+ u16 msix_size;
+ u32 msix_offset;
+ u32 rbar[7];
+ bool pci_2_3;
+ bool virq_disabled;
+ bool reset_works;
+ bool extended_caps;
+ bool bardirty;
+ struct pci_saved_state *pci_saved_state;
+ atomic_t refcnt;
+};
+
+#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
+#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
+#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
+#define irq_is(vdev, type) (vdev->irq_type == type)
+
+extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev);
+extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev);
+
+extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
+ uint32_t flags, unsigned index,
+ unsigned start, unsigned count, void *data);
+
+extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+
+extern int vfio_pci_init_perm_bits(void);
+extern void vfio_pci_uninit_perm_bits(void);
+
+extern int vfio_pci_virqfd_init(void);
+extern void vfio_pci_virqfd_exit(void);
+
+extern int vfio_config_init(struct vfio_pci_device *vdev);
+extern void vfio_config_free(struct vfio_pci_device *vdev);
+#endif /* VFIO_PCI_PRIVATE_H */
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
new file mode 100644
index 000000000000..4362d9e7baa3
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -0,0 +1,269 @@
+/*
+ * VFIO PCI I/O Port & MMIO access
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "vfio_pci_private.h"
+
+/* I/O Port BAR access */
+ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+ int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ void __iomem *io;
+ size_t done = 0;
+
+ if (!pci_resource_start(pdev, bar))
+ return -EINVAL;
+
+ if (pos + count > pci_resource_len(pdev, bar))
+ return -EINVAL;
+
+ if (!vdev->barmap[bar]) {
+ int ret;
+
+ ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+ if (ret)
+ return ret;
+
+ vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+ if (!vdev->barmap[bar]) {
+ pci_release_selected_regions(pdev, 1 << bar);
+ return -EINVAL;
+ }
+ }
+
+ io = vdev->barmap[bar];
+
+ while (count) {
+ int filled;
+
+ if (count >= 3 && !(pos % 4)) {
+ __le32 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 4))
+ return -EFAULT;
+
+ iowrite32(le32_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le32(ioread32(io + pos));
+
+ if (copy_to_user(buf, &val, 4))
+ return -EFAULT;
+ }
+
+ filled = 4;
+
+ } else if ((pos % 2) == 0 && count >= 2) {
+ __le16 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 2))
+ return -EFAULT;
+
+ iowrite16(le16_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le16(ioread16(io + pos));
+
+ if (copy_to_user(buf, &val, 2))
+ return -EFAULT;
+ }
+
+ filled = 2;
+ } else {
+ u8 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 1))
+ return -EFAULT;
+
+ iowrite8(val, io + pos);
+ } else {
+ val = ioread8(io + pos);
+
+ if (copy_to_user(buf, &val, 1))
+ return -EFAULT;
+ }
+
+ filled = 1;
+ }
+
+ count -= filled;
+ done += filled;
+ buf += filled;
+ pos += filled;
+ }
+
+ *ppos += done;
+
+ return done;
+}
+
+/*
+ * MMIO BAR access
+ * We handle two excluded ranges here as well, if the user tries to read
+ * the ROM beyond what PCI tells us is available or the MSI-X table region,
+ * we return 0xFF and writes are dropped.
+ */
+ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+ int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ void __iomem *io;
+ resource_size_t end;
+ size_t done = 0;
+ size_t x_start = 0, x_end = 0; /* excluded range */
+
+ if (!pci_resource_start(pdev, bar))
+ return -EINVAL;
+
+ end = pci_resource_len(pdev, bar);
+
+ if (pos > end)
+ return -EINVAL;
+
+ if (pos == end)
+ return 0;
+
+ if (pos + count > end)
+ count = end - pos;
+
+ if (bar == PCI_ROM_RESOURCE) {
+ io = pci_map_rom(pdev, &x_start);
+ x_end = end;
+ } else {
+ if (!vdev->barmap[bar]) {
+ int ret;
+
+ ret = pci_request_selected_regions(pdev, 1 << bar,
+ "vfio");
+ if (ret)
+ return ret;
+
+ vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+ if (!vdev->barmap[bar]) {
+ pci_release_selected_regions(pdev, 1 << bar);
+ return -EINVAL;
+ }
+ }
+
+ io = vdev->barmap[bar];
+
+ if (bar == vdev->msix_bar) {
+ x_start = vdev->msix_offset;
+ x_end = vdev->msix_offset + vdev->msix_size;
+ }
+ }
+
+ if (!io)
+ return -EINVAL;
+
+ while (count) {
+ size_t fillable, filled;
+
+ if (pos < x_start)
+ fillable = x_start - pos;
+ else if (pos >= x_end)
+ fillable = end - pos;
+ else
+ fillable = 0;
+
+ if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
+ __le32 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 4))
+ goto out;
+
+ iowrite32(le32_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le32(ioread32(io + pos));
+
+ if (copy_to_user(buf, &val, 4))
+ goto out;
+ }
+
+ filled = 4;
+ } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
+ __le16 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 2))
+ goto out;
+
+ iowrite16(le16_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le16(ioread16(io + pos));
+
+ if (copy_to_user(buf, &val, 2))
+ goto out;
+ }
+
+ filled = 2;
+ } else if (fillable) {
+ u8 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 1))
+ goto out;
+
+ iowrite8(val, io + pos);
+ } else {
+ val = ioread8(io + pos);
+
+ if (copy_to_user(buf, &val, 1))
+ goto out;
+ }
+
+ filled = 1;
+ } else {
+ /* Drop writes, fill reads with FF */
+ if (!iswrite) {
+ char val = 0xFF;
+ size_t i;
+
+ for (i = 0; i < x_end - pos; i++) {
+ if (put_user(val, buf + i))
+ goto out;
+ }
+ }
+
+ filled = x_end - pos;
+ }
+
+ count -= filled;
+ done += filled;
+ buf += filled;
+ pos += filled;
+ }
+
+ *ppos += done;
+
+out:
+ if (bar == PCI_ROM_RESOURCE)
+ pci_unmap_rom(pdev, io);
+
+ return count ? -EFAULT : done;
+}
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
new file mode 100644
index 000000000000..9591e2b509d7
--- /dev/null
+++ b/drivers/vfio/vfio.c
@@ -0,0 +1,1420 @@
+/*
+ * VFIO core
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+
+#define DRIVER_VERSION "0.3"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "VFIO - User Level meta-driver"
+
+static struct vfio {
+ struct class *class;
+ struct list_head iommu_drivers_list;
+ struct mutex iommu_drivers_lock;
+ struct list_head group_list;
+ struct idr group_idr;
+ struct mutex group_lock;
+ struct cdev group_cdev;
+ struct device *dev;
+ dev_t devt;
+ struct cdev cdev;
+ wait_queue_head_t release_q;
+} vfio;
+
+struct vfio_iommu_driver {
+ const struct vfio_iommu_driver_ops *ops;
+ struct list_head vfio_next;
+};
+
+struct vfio_container {
+ struct kref kref;
+ struct list_head group_list;
+ struct mutex group_lock;
+ struct vfio_iommu_driver *iommu_driver;
+ void *iommu_data;
+};
+
+struct vfio_group {
+ struct kref kref;
+ int minor;
+ atomic_t container_users;
+ struct iommu_group *iommu_group;
+ struct vfio_container *container;
+ struct list_head device_list;
+ struct mutex device_lock;
+ struct device *dev;
+ struct notifier_block nb;
+ struct list_head vfio_next;
+ struct list_head container_next;
+};
+
+struct vfio_device {
+ struct kref kref;
+ struct device *dev;
+ const struct vfio_device_ops *ops;
+ struct vfio_group *group;
+ struct list_head group_next;
+ void *device_data;
+};
+
+/**
+ * IOMMU driver registration
+ */
+int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+ struct vfio_iommu_driver *driver, *tmp;
+
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->ops = ops;
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+
+ /* Check for duplicates */
+ list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) {
+ if (tmp->ops == ops) {
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ kfree(driver);
+ return -EINVAL;
+ }
+ }
+
+ list_add(&driver->vfio_next, &vfio.iommu_drivers_list);
+
+ mutex_unlock(&vfio.iommu_drivers_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_register_iommu_driver);
+
+void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+ struct vfio_iommu_driver *driver;
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+ if (driver->ops == ops) {
+ list_del(&driver->vfio_next);
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ kfree(driver);
+ return;
+ }
+ }
+ mutex_unlock(&vfio.iommu_drivers_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver);
+
+/**
+ * Group minor allocation/free - both called with vfio.group_lock held
+ */
+static int vfio_alloc_group_minor(struct vfio_group *group)
+{
+ int ret, minor;
+
+again:
+ if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0))
+ return -ENOMEM;
+
+ /* index 0 is used by /dev/vfio/vfio */
+ ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor);
+ if (ret == -EAGAIN)
+ goto again;
+ if (ret || minor > MINORMASK) {
+ if (minor > MINORMASK)
+ idr_remove(&vfio.group_idr, minor);
+ return -ENOSPC;
+ }
+
+ return minor;
+}
+
+static void vfio_free_group_minor(int minor)
+{
+ idr_remove(&vfio.group_idr, minor);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+ unsigned long action, void *data);
+static void vfio_group_get(struct vfio_group *group);
+
+/**
+ * Container objects - containers are created when /dev/vfio/vfio is
+ * opened, but their lifecycle extends until the last user is done, so
+ * it's freed via kref. Must support container/group/device being
+ * closed in any order.
+ */
+static void vfio_container_get(struct vfio_container *container)
+{
+ kref_get(&container->kref);
+}
+
+static void vfio_container_release(struct kref *kref)
+{
+ struct vfio_container *container;
+ container = container_of(kref, struct vfio_container, kref);
+
+ kfree(container);
+}
+
+static void vfio_container_put(struct vfio_container *container)
+{
+ kref_put(&container->kref, vfio_container_release);
+}
+
+/**
+ * Group objects - create, release, get, put, search
+ */
+static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group)
+{
+ struct vfio_group *group, *tmp;
+ struct device *dev;
+ int ret, minor;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&group->kref);
+ INIT_LIST_HEAD(&group->device_list);
+ mutex_init(&group->device_lock);
+ atomic_set(&group->container_users, 0);
+ group->iommu_group = iommu_group;
+
+ group->nb.notifier_call = vfio_iommu_group_notifier;
+
+ /*
+ * blocking notifiers acquire a rwsem around registering and hold
+ * it around callback. Therefore, need to register outside of
+ * vfio.group_lock to avoid A-B/B-A contention. Our callback won't
+ * do anything unless it can find the group in vfio.group_list, so
+ * no harm in registering early.
+ */
+ ret = iommu_group_register_notifier(iommu_group, &group->nb);
+ if (ret) {
+ kfree(group);
+ return ERR_PTR(ret);
+ }
+
+ mutex_lock(&vfio.group_lock);
+
+ minor = vfio_alloc_group_minor(group);
+ if (minor < 0) {
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return ERR_PTR(minor);
+ }
+
+ /* Did we race creating this group? */
+ list_for_each_entry(tmp, &vfio.group_list, vfio_next) {
+ if (tmp->iommu_group == iommu_group) {
+ vfio_group_get(tmp);
+ vfio_free_group_minor(minor);
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return tmp;
+ }
+ }
+
+ dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor),
+ group, "%d", iommu_group_id(iommu_group));
+ if (IS_ERR(dev)) {
+ vfio_free_group_minor(minor);
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return (struct vfio_group *)dev; /* ERR_PTR */
+ }
+
+ group->minor = minor;
+ group->dev = dev;
+
+ list_add(&group->vfio_next, &vfio.group_list);
+
+ mutex_unlock(&vfio.group_lock);
+
+ return group;
+}
+
+static void vfio_group_release(struct kref *kref)
+{
+ struct vfio_group *group = container_of(kref, struct vfio_group, kref);
+
+ WARN_ON(!list_empty(&group->device_list));
+
+ device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor));
+ list_del(&group->vfio_next);
+ vfio_free_group_minor(group->minor);
+
+ mutex_unlock(&vfio.group_lock);
+
+ /*
+ * Unregister outside of lock. A spurious callback is harmless now
+ * that the group is no longer in vfio.group_list.
+ */
+ iommu_group_unregister_notifier(group->iommu_group, &group->nb);
+
+ kfree(group);
+}
+
+static void vfio_group_put(struct vfio_group *group)
+{
+ mutex_lock(&vfio.group_lock);
+ /*
+ * Release needs to unlock to unregister the notifier, so only
+ * unlock if not released.
+ */
+ if (!kref_put(&group->kref, vfio_group_release))
+ mutex_unlock(&vfio.group_lock);
+}
+
+/* Assume group_lock or group reference is held */
+static void vfio_group_get(struct vfio_group *group)
+{
+ kref_get(&group->kref);
+}
+
+/*
+ * Not really a try as we will sleep for mutex, but we need to make
+ * sure the group pointer is valid under lock and get a reference.
+ */
+static struct vfio_group *vfio_group_try_get(struct vfio_group *group)
+{
+ struct vfio_group *target = group;
+
+ mutex_lock(&vfio.group_lock);
+ list_for_each_entry(group, &vfio.group_list, vfio_next) {
+ if (group == target) {
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+ return group;
+ }
+ }
+ mutex_unlock(&vfio.group_lock);
+
+ return NULL;
+}
+
+static
+struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group)
+{
+ struct vfio_group *group;
+
+ mutex_lock(&vfio.group_lock);
+ list_for_each_entry(group, &vfio.group_list, vfio_next) {
+ if (group->iommu_group == iommu_group) {
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+ return group;
+ }
+ }
+ mutex_unlock(&vfio.group_lock);
+
+ return NULL;
+}
+
+static struct vfio_group *vfio_group_get_from_minor(int minor)
+{
+ struct vfio_group *group;
+
+ mutex_lock(&vfio.group_lock);
+ group = idr_find(&vfio.group_idr, minor);
+ if (!group) {
+ mutex_unlock(&vfio.group_lock);
+ return NULL;
+ }
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+
+ return group;
+}
+
+/**
+ * Device objects - create, release, get, put, search
+ */
+static
+struct vfio_device *vfio_group_create_device(struct vfio_group *group,
+ struct device *dev,
+ const struct vfio_device_ops *ops,
+ void *device_data)
+{
+ struct vfio_device *device;
+ int ret;
+
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&device->kref);
+ device->dev = dev;
+ device->group = group;
+ device->ops = ops;
+ device->device_data = device_data;
+
+ ret = dev_set_drvdata(dev, device);
+ if (ret) {
+ kfree(device);
+ return ERR_PTR(ret);
+ }
+
+ /* No need to get group_lock, caller has group reference */
+ vfio_group_get(group);
+
+ mutex_lock(&group->device_lock);
+ list_add(&device->group_next, &group->device_list);
+ mutex_unlock(&group->device_lock);
+
+ return device;
+}
+
+static void vfio_device_release(struct kref *kref)
+{
+ struct vfio_device *device = container_of(kref,
+ struct vfio_device, kref);
+ struct vfio_group *group = device->group;
+
+ mutex_lock(&group->device_lock);
+ list_del(&device->group_next);
+ mutex_unlock(&group->device_lock);
+
+ dev_set_drvdata(device->dev, NULL);
+
+ kfree(device);
+
+ /* vfio_del_group_dev may be waiting for this device */
+ wake_up(&vfio.release_q);
+}
+
+/* Device reference always implies a group reference */
+static void vfio_device_put(struct vfio_device *device)
+{
+ kref_put(&device->kref, vfio_device_release);
+ vfio_group_put(device->group);
+}
+
+static void vfio_device_get(struct vfio_device *device)
+{
+ vfio_group_get(device->group);
+ kref_get(&device->kref);
+}
+
+static struct vfio_device *vfio_group_get_device(struct vfio_group *group,
+ struct device *dev)
+{
+ struct vfio_device *device;
+
+ mutex_lock(&group->device_lock);
+ list_for_each_entry(device, &group->device_list, group_next) {
+ if (device->dev == dev) {
+ vfio_device_get(device);
+ mutex_unlock(&group->device_lock);
+ return device;
+ }
+ }
+ mutex_unlock(&group->device_lock);
+ return NULL;
+}
+
+/*
+ * Whitelist some drivers that we know are safe (no dma) or just sit on
+ * a device. It's not always practical to leave a device within a group
+ * driverless as it could get re-bound to something unsafe.
+ */
+static const char * const vfio_driver_whitelist[] = { "pci-stub" };
+
+static bool vfio_whitelisted_driver(struct device_driver *drv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) {
+ if (!strcmp(drv->name, vfio_driver_whitelist[i]))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * A vfio group is viable for use by userspace if all devices are either
+ * driver-less or bound to a vfio or whitelisted driver. We test the
+ * latter by the existence of a struct vfio_device matching the dev.
+ */
+static int vfio_dev_viable(struct device *dev, void *data)
+{
+ struct vfio_group *group = data;
+ struct vfio_device *device;
+
+ if (!dev->driver || vfio_whitelisted_driver(dev->driver))
+ return 0;
+
+ device = vfio_group_get_device(group, dev);
+ if (device) {
+ vfio_device_put(device);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * Async device support
+ */
+static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev)
+{
+ struct vfio_device *device;
+
+ /* Do we already know about it? We shouldn't */
+ device = vfio_group_get_device(group, dev);
+ if (WARN_ON_ONCE(device)) {
+ vfio_device_put(device);
+ return 0;
+ }
+
+ /* Nothing to do for idle groups */
+ if (!atomic_read(&group->container_users))
+ return 0;
+
+ /* TODO Prevent device auto probing */
+ WARN("Device %s added to live group %d!\n", dev_name(dev),
+ iommu_group_id(group->iommu_group));
+
+ return 0;
+}
+
+static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev)
+{
+ struct vfio_device *device;
+
+ /*
+ * Expect to fall out here. If a device was in use, it would
+ * have been bound to a vfio sub-driver, which would have blocked
+ * in .remove at vfio_del_group_dev. Sanity check that we no
+ * longer track the device, so it's safe to remove.
+ */
+ device = vfio_group_get_device(group, dev);
+ if (likely(!device))
+ return 0;
+
+ WARN("Device %s removed from live group %d!\n", dev_name(dev),
+ iommu_group_id(group->iommu_group));
+
+ vfio_device_put(device);
+ return 0;
+}
+
+static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev)
+{
+ /* We don't care what happens when the group isn't in use */
+ if (!atomic_read(&group->container_users))
+ return 0;
+
+ return vfio_dev_viable(dev, group);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct vfio_group *group = container_of(nb, struct vfio_group, nb);
+ struct device *dev = data;
+
+ /*
+ * Need to go through a group_lock lookup to get a reference or
+ * we risk racing a group being removed. Leave a WARN_ON for
+ * debuging, but if the group no longer exists, a spurious notify
+ * is harmless.
+ */
+ group = vfio_group_try_get(group);
+ if (WARN_ON(!group))
+ return NOTIFY_OK;
+
+ switch (action) {
+ case IOMMU_GROUP_NOTIFY_ADD_DEVICE:
+ vfio_group_nb_add_dev(group, dev);
+ break;
+ case IOMMU_GROUP_NOTIFY_DEL_DEVICE:
+ vfio_group_nb_del_dev(group, dev);
+ break;
+ case IOMMU_GROUP_NOTIFY_BIND_DRIVER:
+ pr_debug("%s: Device %s, group %d binding to driver\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group));
+ break;
+ case IOMMU_GROUP_NOTIFY_BOUND_DRIVER:
+ pr_debug("%s: Device %s, group %d bound to driver %s\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group), dev->driver->name);
+ BUG_ON(vfio_group_nb_verify(group, dev));
+ break;
+ case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER:
+ pr_debug("%s: Device %s, group %d unbinding from driver %s\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group), dev->driver->name);
+ break;
+ case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER:
+ pr_debug("%s: Device %s, group %d unbound from driver\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group));
+ /*
+ * XXX An unbound device in a live group is ok, but we'd
+ * really like to avoid the above BUG_ON by preventing other
+ * drivers from binding to it. Once that occurs, we have to
+ * stop the system to maintain isolation. At a minimum, we'd
+ * want a toggle to disable driver auto probe for this device.
+ */
+ break;
+ }
+
+ vfio_group_put(group);
+ return NOTIFY_OK;
+}
+
+/**
+ * VFIO driver API
+ */
+int vfio_add_group_dev(struct device *dev,
+ const struct vfio_device_ops *ops, void *device_data)
+{
+ struct iommu_group *iommu_group;
+ struct vfio_group *group;
+ struct vfio_device *device;
+
+ iommu_group = iommu_group_get(dev);
+ if (!iommu_group)
+ return -EINVAL;
+
+ group = vfio_group_get_from_iommu(iommu_group);
+ if (!group) {
+ group = vfio_create_group(iommu_group);
+ if (IS_ERR(group)) {
+ iommu_group_put(iommu_group);
+ return PTR_ERR(group);
+ }
+ }
+
+ device = vfio_group_get_device(group, dev);
+ if (device) {
+ WARN(1, "Device %s already exists on group %d\n",
+ dev_name(dev), iommu_group_id(iommu_group));
+ vfio_device_put(device);
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return -EBUSY;
+ }
+
+ device = vfio_group_create_device(group, dev, ops, device_data);
+ if (IS_ERR(device)) {
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return PTR_ERR(device);
+ }
+
+ /*
+ * Added device holds reference to iommu_group and vfio_device
+ * (which in turn holds reference to vfio_group). Drop extra
+ * group reference used while acquiring device.
+ */
+ vfio_group_put(group);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_add_group_dev);
+
+/* Test whether a struct device is present in our tracking */
+static bool vfio_dev_present(struct device *dev)
+{
+ struct iommu_group *iommu_group;
+ struct vfio_group *group;
+ struct vfio_device *device;
+
+ iommu_group = iommu_group_get(dev);
+ if (!iommu_group)
+ return false;
+
+ group = vfio_group_get_from_iommu(iommu_group);
+ if (!group) {
+ iommu_group_put(iommu_group);
+ return false;
+ }
+
+ device = vfio_group_get_device(group, dev);
+ if (!device) {
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return false;
+ }
+
+ vfio_device_put(device);
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return true;
+}
+
+/*
+ * Decrement the device reference count and wait for the device to be
+ * removed. Open file descriptors for the device... */
+void *vfio_del_group_dev(struct device *dev)
+{
+ struct vfio_device *device = dev_get_drvdata(dev);
+ struct vfio_group *group = device->group;
+ struct iommu_group *iommu_group = group->iommu_group;
+ void *device_data = device->device_data;
+
+ vfio_device_put(device);
+
+ /* TODO send a signal to encourage this to be released */
+ wait_event(vfio.release_q, !vfio_dev_present(dev));
+
+ iommu_group_put(iommu_group);
+
+ return device_data;
+}
+EXPORT_SYMBOL_GPL(vfio_del_group_dev);
+
+/**
+ * VFIO base fd, /dev/vfio/vfio
+ */
+static long vfio_ioctl_check_extension(struct vfio_container *container,
+ unsigned long arg)
+{
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+ long ret = 0;
+
+ switch (arg) {
+ /* No base extensions yet */
+ default:
+ /*
+ * If no driver is set, poll all registered drivers for
+ * extensions and return the first positive result. If
+ * a driver is already set, further queries will be passed
+ * only to that driver.
+ */
+ if (!driver) {
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list,
+ vfio_next) {
+ if (!try_module_get(driver->ops->owner))
+ continue;
+
+ ret = driver->ops->ioctl(NULL,
+ VFIO_CHECK_EXTENSION,
+ arg);
+ module_put(driver->ops->owner);
+ if (ret > 0)
+ break;
+ }
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ } else
+ ret = driver->ops->ioctl(container->iommu_data,
+ VFIO_CHECK_EXTENSION, arg);
+ }
+
+ return ret;
+}
+
+/* hold container->group_lock */
+static int __vfio_container_attach_groups(struct vfio_container *container,
+ struct vfio_iommu_driver *driver,
+ void *data)
+{
+ struct vfio_group *group;
+ int ret = -ENODEV;
+
+ list_for_each_entry(group, &container->group_list, container_next) {
+ ret = driver->ops->attach_group(data, group->iommu_group);
+ if (ret)
+ goto unwind;
+ }
+
+ return ret;
+
+unwind:
+ list_for_each_entry_continue_reverse(group, &container->group_list,
+ container_next) {
+ driver->ops->detach_group(data, group->iommu_group);
+ }
+
+ return ret;
+}
+
+static long vfio_ioctl_set_iommu(struct vfio_container *container,
+ unsigned long arg)
+{
+ struct vfio_iommu_driver *driver;
+ long ret = -ENODEV;
+
+ mutex_lock(&container->group_lock);
+
+ /*
+ * The container is designed to be an unprivileged interface while
+ * the group can be assigned to specific users. Therefore, only by
+ * adding a group to a container does the user get the privilege of
+ * enabling the iommu, which may allocate finite resources. There
+ * is no unset_iommu, but by removing all the groups from a container,
+ * the container is deprivileged and returns to an unset state.
+ */
+ if (list_empty(&container->group_list) || container->iommu_driver) {
+ mutex_unlock(&container->group_lock);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+ void *data;
+
+ if (!try_module_get(driver->ops->owner))
+ continue;
+
+ /*
+ * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION,
+ * so test which iommu driver reported support for this
+ * extension and call open on them. We also pass them the
+ * magic, allowing a single driver to support multiple
+ * interfaces if they'd like.
+ */
+ if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) {
+ module_put(driver->ops->owner);
+ continue;
+ }
+
+ /* module reference holds the driver we're working on */
+ mutex_unlock(&vfio.iommu_drivers_lock);
+
+ data = driver->ops->open(arg);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ module_put(driver->ops->owner);
+ goto skip_drivers_unlock;
+ }
+
+ ret = __vfio_container_attach_groups(container, driver, data);
+ if (!ret) {
+ container->iommu_driver = driver;
+ container->iommu_data = data;
+ } else {
+ driver->ops->release(data);
+ module_put(driver->ops->owner);
+ }
+
+ goto skip_drivers_unlock;
+ }
+
+ mutex_unlock(&vfio.iommu_drivers_lock);
+skip_drivers_unlock:
+ mutex_unlock(&container->group_lock);
+
+ return ret;
+}
+
+static long vfio_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver;
+ void *data;
+ long ret = -EINVAL;
+
+ if (!container)
+ return ret;
+
+ driver = container->iommu_driver;
+ data = container->iommu_data;
+
+ switch (cmd) {
+ case VFIO_GET_API_VERSION:
+ ret = VFIO_API_VERSION;
+ break;
+ case VFIO_CHECK_EXTENSION:
+ ret = vfio_ioctl_check_extension(container, arg);
+ break;
+ case VFIO_SET_IOMMU:
+ ret = vfio_ioctl_set_iommu(container, arg);
+ break;
+ default:
+ if (driver) /* passthrough all unrecognized ioctls */
+ ret = driver->ops->ioctl(data, cmd, arg);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_fops_open(struct inode *inode, struct file *filep)
+{
+ struct vfio_container *container;
+
+ container = kzalloc(sizeof(*container), GFP_KERNEL);
+ if (!container)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&container->group_list);
+ mutex_init(&container->group_lock);
+ kref_init(&container->kref);
+
+ filep->private_data = container;
+
+ return 0;
+}
+
+static int vfio_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_container *container = filep->private_data;
+
+ filep->private_data = NULL;
+
+ vfio_container_put(container);
+
+ return 0;
+}
+
+/*
+ * Once an iommu driver is set, we optionally pass read/write/mmap
+ * on to the driver, allowing management interfaces beyond ioctl.
+ */
+static ssize_t vfio_fops_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->read))
+ return -EINVAL;
+
+ return driver->ops->read(container->iommu_data, buf, count, ppos);
+}
+
+static ssize_t vfio_fops_write(struct file *filep, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->write))
+ return -EINVAL;
+
+ return driver->ops->write(container->iommu_data, buf, count, ppos);
+}
+
+static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->mmap))
+ return -EINVAL;
+
+ return driver->ops->mmap(container->iommu_data, vma);
+}
+
+static const struct file_operations vfio_fops = {
+ .owner = THIS_MODULE,
+ .open = vfio_fops_open,
+ .release = vfio_fops_release,
+ .read = vfio_fops_read,
+ .write = vfio_fops_write,
+ .unlocked_ioctl = vfio_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_fops_compat_ioctl,
+#endif
+ .mmap = vfio_fops_mmap,
+};
+
+/**
+ * VFIO Group fd, /dev/vfio/$GROUP
+ */
+static void __vfio_group_unset_container(struct vfio_group *group)
+{
+ struct vfio_container *container = group->container;
+ struct vfio_iommu_driver *driver;
+
+ mutex_lock(&container->group_lock);
+
+ driver = container->iommu_driver;
+ if (driver)
+ driver->ops->detach_group(container->iommu_data,
+ group->iommu_group);
+
+ group->container = NULL;
+ list_del(&group->container_next);
+
+ /* Detaching the last group deprivileges a container, remove iommu */
+ if (driver && list_empty(&container->group_list)) {
+ driver->ops->release(container->iommu_data);
+ module_put(driver->ops->owner);
+ container->iommu_driver = NULL;
+ container->iommu_data = NULL;
+ }
+
+ mutex_unlock(&container->group_lock);
+
+ vfio_container_put(container);
+}
+
+/*
+ * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or
+ * if there was no container to unset. Since the ioctl is called on
+ * the group, we know that still exists, therefore the only valid
+ * transition here is 1->0.
+ */
+static int vfio_group_unset_container(struct vfio_group *group)
+{
+ int users = atomic_cmpxchg(&group->container_users, 1, 0);
+
+ if (!users)
+ return -EINVAL;
+ if (users != 1)
+ return -EBUSY;
+
+ __vfio_group_unset_container(group);
+
+ return 0;
+}
+
+/*
+ * When removing container users, anything that removes the last user
+ * implicitly removes the group from the container. That is, if the
+ * group file descriptor is closed, as well as any device file descriptors,
+ * the group is free.
+ */
+static void vfio_group_try_dissolve_container(struct vfio_group *group)
+{
+ if (0 == atomic_dec_if_positive(&group->container_users))
+ __vfio_group_unset_container(group);
+}
+
+static int vfio_group_set_container(struct vfio_group *group, int container_fd)
+{
+ struct file *filep;
+ struct vfio_container *container;
+ struct vfio_iommu_driver *driver;
+ int ret = 0;
+
+ if (atomic_read(&group->container_users))
+ return -EINVAL;
+
+ filep = fget(container_fd);
+ if (!filep)
+ return -EBADF;
+
+ /* Sanity check, is this really our fd? */
+ if (filep->f_op != &vfio_fops) {
+ fput(filep);
+ return -EINVAL;
+ }
+
+ container = filep->private_data;
+ WARN_ON(!container); /* fget ensures we don't race vfio_release */
+
+ mutex_lock(&container->group_lock);
+
+ driver = container->iommu_driver;
+ if (driver) {
+ ret = driver->ops->attach_group(container->iommu_data,
+ group->iommu_group);
+ if (ret)
+ goto unlock_out;
+ }
+
+ group->container = container;
+ list_add(&group->container_next, &container->group_list);
+
+ /* Get a reference on the container and mark a user within the group */
+ vfio_container_get(container);
+ atomic_inc(&group->container_users);
+
+unlock_out:
+ mutex_unlock(&container->group_lock);
+ fput(filep);
+
+ return ret;
+}
+
+static bool vfio_group_viable(struct vfio_group *group)
+{
+ return (iommu_group_for_each_dev(group->iommu_group,
+ group, vfio_dev_viable) == 0);
+}
+
+static const struct file_operations vfio_device_fops;
+
+static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
+{
+ struct vfio_device *device;
+ struct file *filep;
+ int ret = -ENODEV;
+
+ if (0 == atomic_read(&group->container_users) ||
+ !group->container->iommu_driver || !vfio_group_viable(group))
+ return -EINVAL;
+
+ mutex_lock(&group->device_lock);
+ list_for_each_entry(device, &group->device_list, group_next) {
+ if (strcmp(dev_name(device->dev), buf))
+ continue;
+
+ ret = device->ops->open(device->device_data);
+ if (ret)
+ break;
+ /*
+ * We can't use anon_inode_getfd() because we need to modify
+ * the f_mode flags directly to allow more than just ioctls
+ */
+ ret = get_unused_fd();
+ if (ret < 0) {
+ device->ops->release(device->device_data);
+ break;
+ }
+
+ filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
+ device, O_RDWR);
+ if (IS_ERR(filep)) {
+ put_unused_fd(ret);
+ ret = PTR_ERR(filep);
+ device->ops->release(device->device_data);
+ break;
+ }
+
+ /*
+ * TODO: add an anon_inode interface to do this.
+ * Appears to be missing by lack of need rather than
+ * explicitly prevented. Now there's need.
+ */
+ filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+
+ fd_install(ret, filep);
+
+ vfio_device_get(device);
+ atomic_inc(&group->container_users);
+ break;
+ }
+ mutex_unlock(&group->device_lock);
+
+ return ret;
+}
+
+static long vfio_group_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_group *group = filep->private_data;
+ long ret = -ENOTTY;
+
+ switch (cmd) {
+ case VFIO_GROUP_GET_STATUS:
+ {
+ struct vfio_group_status status;
+ unsigned long minsz;
+
+ minsz = offsetofend(struct vfio_group_status, flags);
+
+ if (copy_from_user(&status, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (status.argsz < minsz)
+ return -EINVAL;
+
+ status.flags = 0;
+
+ if (vfio_group_viable(group))
+ status.flags |= VFIO_GROUP_FLAGS_VIABLE;
+
+ if (group->container)
+ status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET;
+
+ if (copy_to_user((void __user *)arg, &status, minsz))
+ return -EFAULT;
+
+ ret = 0;
+ break;
+ }
+ case VFIO_GROUP_SET_CONTAINER:
+ {
+ int fd;
+
+ if (get_user(fd, (int __user *)arg))
+ return -EFAULT;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ ret = vfio_group_set_container(group, fd);
+ break;
+ }
+ case VFIO_GROUP_UNSET_CONTAINER:
+ ret = vfio_group_unset_container(group);
+ break;
+ case VFIO_GROUP_GET_DEVICE_FD:
+ {
+ char *buf;
+
+ buf = strndup_user((const char __user *)arg, PAGE_SIZE);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = vfio_group_get_device_fd(group, buf);
+ kfree(buf);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_group_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_group_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_group_fops_open(struct inode *inode, struct file *filep)
+{
+ struct vfio_group *group;
+
+ group = vfio_group_get_from_minor(iminor(inode));
+ if (!group)
+ return -ENODEV;
+
+ if (group->container) {
+ vfio_group_put(group);
+ return -EBUSY;
+ }
+
+ filep->private_data = group;
+
+ return 0;
+}
+
+static int vfio_group_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_group *group = filep->private_data;
+
+ filep->private_data = NULL;
+
+ vfio_group_try_dissolve_container(group);
+
+ vfio_group_put(group);
+
+ return 0;
+}
+
+static const struct file_operations vfio_group_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = vfio_group_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_group_fops_compat_ioctl,
+#endif
+ .open = vfio_group_fops_open,
+ .release = vfio_group_fops_release,
+};
+
+/**
+ * VFIO Device fd
+ */
+static int vfio_device_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_device *device = filep->private_data;
+
+ device->ops->release(device->device_data);
+
+ vfio_group_try_dissolve_container(device->group);
+
+ vfio_device_put(device);
+
+ return 0;
+}
+
+static long vfio_device_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->ioctl))
+ return -EINVAL;
+
+ return device->ops->ioctl(device->device_data, cmd, arg);
+}
+
+static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->read))
+ return -EINVAL;
+
+ return device->ops->read(device->device_data, buf, count, ppos);
+}
+
+static ssize_t vfio_device_fops_write(struct file *filep,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->write))
+ return -EINVAL;
+
+ return device->ops->write(device->device_data, buf, count, ppos);
+}
+
+static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->mmap))
+ return -EINVAL;
+
+ return device->ops->mmap(device->device_data, vma);
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_device_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_device_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static const struct file_operations vfio_device_fops = {
+ .owner = THIS_MODULE,
+ .release = vfio_device_fops_release,
+ .read = vfio_device_fops_read,
+ .write = vfio_device_fops_write,
+ .unlocked_ioctl = vfio_device_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_device_fops_compat_ioctl,
+#endif
+ .mmap = vfio_device_fops_mmap,
+};
+
+/**
+ * Module/class support
+ */
+static char *vfio_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev));
+}
+
+static int __init vfio_init(void)
+{
+ int ret;
+
+ idr_init(&vfio.group_idr);
+ mutex_init(&vfio.group_lock);
+ mutex_init(&vfio.iommu_drivers_lock);
+ INIT_LIST_HEAD(&vfio.group_list);
+ INIT_LIST_HEAD(&vfio.iommu_drivers_list);
+ init_waitqueue_head(&vfio.release_q);
+
+ vfio.class = class_create(THIS_MODULE, "vfio");
+ if (IS_ERR(vfio.class)) {
+ ret = PTR_ERR(vfio.class);
+ goto err_class;
+ }
+
+ vfio.class->devnode = vfio_devnode;
+
+ ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio");
+ if (ret)
+ goto err_base_chrdev;
+
+ cdev_init(&vfio.cdev, &vfio_fops);
+ ret = cdev_add(&vfio.cdev, vfio.devt, 1);
+ if (ret)
+ goto err_base_cdev;
+
+ vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio");
+ if (IS_ERR(vfio.dev)) {
+ ret = PTR_ERR(vfio.dev);
+ goto err_base_dev;
+ }
+
+ /* /dev/vfio/$GROUP */
+ cdev_init(&vfio.group_cdev, &vfio_group_fops);
+ ret = cdev_add(&vfio.group_cdev,
+ MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1);
+ if (ret)
+ goto err_groups_cdev;
+
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ /*
+ * Attempt to load known iommu-drivers. This gives us a working
+ * environment without the user needing to explicitly load iommu
+ * drivers.
+ */
+ request_module_nowait("vfio_iommu_type1");
+
+ return 0;
+
+err_groups_cdev:
+ device_destroy(vfio.class, vfio.devt);
+err_base_dev:
+ cdev_del(&vfio.cdev);
+err_base_cdev:
+ unregister_chrdev_region(vfio.devt, MINORMASK);
+err_base_chrdev:
+ class_destroy(vfio.class);
+ vfio.class = NULL;
+err_class:
+ return ret;
+}
+
+static void __exit vfio_cleanup(void)
+{
+ WARN_ON(!list_empty(&vfio.group_list));
+
+ idr_destroy(&vfio.group_idr);
+ cdev_del(&vfio.group_cdev);
+ device_destroy(vfio.class, vfio.devt);
+ cdev_del(&vfio.cdev);
+ unregister_chrdev_region(vfio.devt, MINORMASK);
+ class_destroy(vfio.class);
+ vfio.class = NULL;
+}
+
+module_init(vfio_init);
+module_exit(vfio_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
new file mode 100644
index 000000000000..6f3fbc48a6c7
--- /dev/null
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -0,0 +1,753 @@
+/*
+ * VFIO: IOMMU DMA mapping support for Type1 IOMMU
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ *
+ * We arbitrarily define a Type1 IOMMU as one matching the below code.
+ * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel
+ * VT-d, but that makes it harder to re-use as theoretically anyone
+ * implementing a similar IOMMU could make use of this. We expect the
+ * IOMMU to support the IOMMU API and have few to no restrictions around
+ * the IOVA range that can be mapped. The Type1 IOMMU is currently
+ * optimized for relatively static mappings of a userspace process with
+ * userpsace pages pinned into memory. We also assume devices and IOMMU
+ * domains are PCI based as the IOMMU API is still centered around a
+ * device/bus interface rather than a group interface.
+ */
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pci.h> /* pci_bus_type */
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "Type1 IOMMU driver for VFIO"
+
+static bool allow_unsafe_interrupts;
+module_param_named(allow_unsafe_interrupts,
+ allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(allow_unsafe_interrupts,
+ "Enable VFIO IOMMU support for on platforms without interrupt remapping support.");
+
+struct vfio_iommu {
+ struct iommu_domain *domain;
+ struct mutex lock;
+ struct list_head dma_list;
+ struct list_head group_list;
+ bool cache;
+};
+
+struct vfio_dma {
+ struct list_head next;
+ dma_addr_t iova; /* Device address */
+ unsigned long vaddr; /* Process virtual addr */
+ long npage; /* Number of pages */
+ int prot; /* IOMMU_READ/WRITE */
+};
+
+struct vfio_group {
+ struct iommu_group *iommu_group;
+ struct list_head next;
+};
+
+/*
+ * This code handles mapping and unmapping of user data buffers
+ * into DMA'ble space using the IOMMU
+ */
+
+#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT)
+
+struct vwork {
+ struct mm_struct *mm;
+ long npage;
+ struct work_struct work;
+};
+
+/* delayed decrement/increment for locked_vm */
+static void vfio_lock_acct_bg(struct work_struct *work)
+{
+ struct vwork *vwork = container_of(work, struct vwork, work);
+ struct mm_struct *mm;
+
+ mm = vwork->mm;
+ down_write(&mm->mmap_sem);
+ mm->locked_vm += vwork->npage;
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ kfree(vwork);
+}
+
+static void vfio_lock_acct(long npage)
+{
+ struct vwork *vwork;
+ struct mm_struct *mm;
+
+ if (!current->mm)
+ return; /* process exited */
+
+ if (down_write_trylock(&current->mm->mmap_sem)) {
+ current->mm->locked_vm += npage;
+ up_write(&current->mm->mmap_sem);
+ return;
+ }
+
+ /*
+ * Couldn't get mmap_sem lock, so must setup to update
+ * mm->locked_vm later. If locked_vm were atomic, we
+ * wouldn't need this silliness
+ */
+ vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
+ if (!vwork)
+ return;
+ mm = get_task_mm(current);
+ if (!mm) {
+ kfree(vwork);
+ return;
+ }
+ INIT_WORK(&vwork->work, vfio_lock_acct_bg);
+ vwork->mm = mm;
+ vwork->npage = npage;
+ schedule_work(&vwork->work);
+}
+
+/*
+ * Some mappings aren't backed by a struct page, for example an mmap'd
+ * MMIO range for our own or another device. These use a different
+ * pfn conversion and shouldn't be tracked as locked pages.
+ */
+static bool is_invalid_reserved_pfn(unsigned long pfn)
+{
+ if (pfn_valid(pfn)) {
+ bool reserved;
+ struct page *tail = pfn_to_page(pfn);
+ struct page *head = compound_trans_head(tail);
+ reserved = !!(PageReserved(head));
+ if (head != tail) {
+ /*
+ * "head" is not a dangling pointer
+ * (compound_trans_head takes care of that)
+ * but the hugepage may have been split
+ * from under us (and we may not hold a
+ * reference count on the head page so it can
+ * be reused before we run PageReferenced), so
+ * we've to check PageTail before returning
+ * what we just read.
+ */
+ smp_rmb();
+ if (PageTail(tail))
+ return reserved;
+ }
+ return PageReserved(tail);
+ }
+
+ return true;
+}
+
+static int put_pfn(unsigned long pfn, int prot)
+{
+ if (!is_invalid_reserved_pfn(pfn)) {
+ struct page *page = pfn_to_page(pfn);
+ if (prot & IOMMU_WRITE)
+ SetPageDirty(page);
+ put_page(page);
+ return 1;
+ }
+ return 0;
+}
+
+/* Unmap DMA region */
+static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+ long npage, int prot)
+{
+ long i, unlocked = 0;
+
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE) {
+ unsigned long pfn;
+
+ pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT;
+ if (pfn) {
+ iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+ unlocked += put_pfn(pfn, prot);
+ }
+ }
+ return unlocked;
+}
+
+static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+ long npage, int prot)
+{
+ long unlocked;
+
+ unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot);
+ vfio_lock_acct(-unlocked);
+}
+
+static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
+{
+ struct page *page[1];
+ struct vm_area_struct *vma;
+ int ret = -EFAULT;
+
+ if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) {
+ *pfn = page_to_pfn(page[0]);
+ return 0;
+ }
+
+ down_read(&current->mm->mmap_sem);
+
+ vma = find_vma_intersection(current->mm, vaddr, vaddr + 1);
+
+ if (vma && vma->vm_flags & VM_PFNMAP) {
+ *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (is_invalid_reserved_pfn(*pfn))
+ ret = 0;
+ }
+
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+
+/* Map DMA region */
+static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova,
+ unsigned long vaddr, long npage, int prot)
+{
+ dma_addr_t start = iova;
+ long i, locked = 0;
+ int ret;
+
+ /* Verify that pages are not already mapped */
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE)
+ if (iommu_iova_to_phys(iommu->domain, iova))
+ return -EBUSY;
+
+ iova = start;
+
+ if (iommu->cache)
+ prot |= IOMMU_CACHE;
+
+ /*
+ * XXX We break mappings into pages and use get_user_pages_fast to
+ * pin the pages in memory. It's been suggested that mlock might
+ * provide a more efficient mechanism, but nothing prevents the
+ * user from munlocking the pages, which could then allow the user
+ * access to random host memory. We also have no guarantee from the
+ * IOMMU API that the iommu driver can unmap sub-pages of previous
+ * mappings. This means we might lose an entire range if a single
+ * page within it is unmapped. Single page mappings are inefficient,
+ * but provide the most flexibility for now.
+ */
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) {
+ unsigned long pfn = 0;
+
+ ret = vaddr_get_pfn(vaddr, prot, &pfn);
+ if (ret) {
+ __vfio_dma_do_unmap(iommu, start, i, prot);
+ return ret;
+ }
+
+ /*
+ * Only add actual locked pages to accounting
+ * XXX We're effectively marking a page locked for every
+ * IOVA page even though it's possible the user could be
+ * backing multiple IOVAs with the same vaddr. This over-
+ * penalizes the user process, but we currently have no
+ * easy way to do this properly.
+ */
+ if (!is_invalid_reserved_pfn(pfn))
+ locked++;
+
+ ret = iommu_map(iommu->domain, iova,
+ (phys_addr_t)pfn << PAGE_SHIFT,
+ PAGE_SIZE, prot);
+ if (ret) {
+ /* Back out mappings on error */
+ put_pfn(pfn, prot);
+ __vfio_dma_do_unmap(iommu, start, i, prot);
+ return ret;
+ }
+ }
+ vfio_lock_acct(locked);
+ return 0;
+}
+
+static inline bool ranges_overlap(dma_addr_t start1, size_t size1,
+ dma_addr_t start2, size_t size2)
+{
+ if (start1 < start2)
+ return (start2 - start1 < size1);
+ else if (start2 < start1)
+ return (start1 - start2 < size2);
+ return (size1 > 0 && size2 > 0);
+}
+
+static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
+ dma_addr_t start, size_t size)
+{
+ struct vfio_dma *dma;
+
+ list_for_each_entry(dma, &iommu->dma_list, next) {
+ if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+ start, size))
+ return dma;
+ }
+ return NULL;
+}
+
+static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
+ size_t size, struct vfio_dma *dma)
+{
+ struct vfio_dma *split;
+ long npage_lo, npage_hi;
+
+ /* Existing dma region is completely covered, unmap all */
+ if (start <= dma->iova &&
+ start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+ vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+ list_del(&dma->next);
+ npage_lo = dma->npage;
+ kfree(dma);
+ return npage_lo;
+ }
+
+ /* Overlap low address of existing range */
+ if (start <= dma->iova) {
+ size_t overlap;
+
+ overlap = start + size - dma->iova;
+ npage_lo = overlap >> PAGE_SHIFT;
+
+ vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot);
+ dma->iova += overlap;
+ dma->vaddr += overlap;
+ dma->npage -= npage_lo;
+ return npage_lo;
+ }
+
+ /* Overlap high address of existing range */
+ if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+ size_t overlap;
+
+ overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start;
+ npage_hi = overlap >> PAGE_SHIFT;
+
+ vfio_dma_unmap(iommu, start, npage_hi, dma->prot);
+ dma->npage -= npage_hi;
+ return npage_hi;
+ }
+
+ /* Split existing */
+ npage_lo = (start - dma->iova) >> PAGE_SHIFT;
+ npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo;
+
+ split = kzalloc(sizeof *split, GFP_KERNEL);
+ if (!split)
+ return -ENOMEM;
+
+ vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot);
+
+ dma->npage = npage_lo;
+
+ split->npage = npage_hi;
+ split->iova = start + size;
+ split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size;
+ split->prot = dma->prot;
+ list_add(&split->next, &iommu->dma_list);
+ return size >> PAGE_SHIFT;
+}
+
+static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
+ struct vfio_iommu_type1_dma_unmap *unmap)
+{
+ long ret = 0, npage = unmap->size >> PAGE_SHIFT;
+ struct vfio_dma *dma, *tmp;
+ uint64_t mask;
+
+ mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+ if (unmap->iova & mask)
+ return -EINVAL;
+ if (unmap->size & mask)
+ return -EINVAL;
+
+ /* XXX We still break these down into PAGE_SIZE */
+ WARN_ON(mask & PAGE_MASK);
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) {
+ if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+ unmap->iova, unmap->size)) {
+ ret = vfio_remove_dma_overlap(iommu, unmap->iova,
+ unmap->size, dma);
+ if (ret > 0)
+ npage -= ret;
+ if (ret < 0 || npage == 0)
+ break;
+ }
+ }
+ mutex_unlock(&iommu->lock);
+ return ret > 0 ? 0 : (int)ret;
+}
+
+static int vfio_dma_do_map(struct vfio_iommu *iommu,
+ struct vfio_iommu_type1_dma_map *map)
+{
+ struct vfio_dma *dma, *pdma = NULL;
+ dma_addr_t iova = map->iova;
+ unsigned long locked, lock_limit, vaddr = map->vaddr;
+ size_t size = map->size;
+ int ret = 0, prot = 0;
+ uint64_t mask;
+ long npage;
+
+ mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+ /* READ/WRITE from device perspective */
+ if (map->flags & VFIO_DMA_MAP_FLAG_WRITE)
+ prot |= IOMMU_WRITE;
+ if (map->flags & VFIO_DMA_MAP_FLAG_READ)
+ prot |= IOMMU_READ;
+
+ if (!prot)
+ return -EINVAL; /* No READ/WRITE? */
+
+ if (vaddr & mask)
+ return -EINVAL;
+ if (iova & mask)
+ return -EINVAL;
+ if (size & mask)
+ return -EINVAL;
+
+ /* XXX We still break these down into PAGE_SIZE */
+ WARN_ON(mask & PAGE_MASK);
+
+ /* Don't allow IOVA wrap */
+ if (iova + size && iova + size < iova)
+ return -EINVAL;
+
+ /* Don't allow virtual address wrap */
+ if (vaddr + size && vaddr + size < vaddr)
+ return -EINVAL;
+
+ npage = size >> PAGE_SHIFT;
+ if (!npage)
+ return -EINVAL;
+
+ mutex_lock(&iommu->lock);
+
+ if (vfio_find_dma(iommu, iova, size)) {
+ ret = -EBUSY;
+ goto out_lock;
+ }
+
+ /* account for locked pages */
+ locked = current->mm->locked_vm + npage;
+ lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
+ pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
+ __func__, rlimit(RLIMIT_MEMLOCK));
+ ret = -ENOMEM;
+ goto out_lock;
+ }
+
+ ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot);
+ if (ret)
+ goto out_lock;
+
+ /* Check if we abut a region below - nothing below 0 */
+ if (iova) {
+ dma = vfio_find_dma(iommu, iova - 1, 1);
+ if (dma && dma->prot == prot &&
+ dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) {
+
+ dma->npage += npage;
+ iova = dma->iova;
+ vaddr = dma->vaddr;
+ npage = dma->npage;
+ size = NPAGE_TO_SIZE(npage);
+
+ pdma = dma;
+ }
+ }
+
+ /* Check if we abut a region above - nothing above ~0 + 1 */
+ if (iova + size) {
+ dma = vfio_find_dma(iommu, iova + size, 1);
+ if (dma && dma->prot == prot &&
+ dma->vaddr == vaddr + size) {
+
+ dma->npage += npage;
+ dma->iova = iova;
+ dma->vaddr = vaddr;
+
+ /*
+ * If merged above and below, remove previously
+ * merged entry. New entry covers it.
+ */
+ if (pdma) {
+ list_del(&pdma->next);
+ kfree(pdma);
+ }
+ pdma = dma;
+ }
+ }
+
+ /* Isolated, new region */
+ if (!pdma) {
+ dma = kzalloc(sizeof *dma, GFP_KERNEL);
+ if (!dma) {
+ ret = -ENOMEM;
+ vfio_dma_unmap(iommu, iova, npage, prot);
+ goto out_lock;
+ }
+
+ dma->npage = npage;
+ dma->iova = iova;
+ dma->vaddr = vaddr;
+ dma->prot = prot;
+ list_add(&dma->next, &iommu->dma_list);
+ }
+
+out_lock:
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
+static int vfio_iommu_type1_attach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group, *tmp;
+ int ret;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry(tmp, &iommu->group_list, next) {
+ if (tmp->iommu_group == iommu_group) {
+ mutex_unlock(&iommu->lock);
+ kfree(group);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * TODO: Domain have capabilities that might change as we add
+ * groups (see iommu->cache, currently never set). Check for
+ * them and potentially disallow groups to be attached when it
+ * would change capabilities (ugh).
+ */
+ ret = iommu_attach_group(iommu->domain, iommu_group);
+ if (ret) {
+ mutex_unlock(&iommu->lock);
+ kfree(group);
+ return ret;
+ }
+
+ group->iommu_group = iommu_group;
+ list_add(&group->next, &iommu->group_list);
+
+ mutex_unlock(&iommu->lock);
+
+ return 0;
+}
+
+static void vfio_iommu_type1_detach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group;
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry(group, &iommu->group_list, next) {
+ if (group->iommu_group == iommu_group) {
+ iommu_detach_group(iommu->domain, iommu_group);
+ list_del(&group->next);
+ kfree(group);
+ break;
+ }
+ }
+
+ mutex_unlock(&iommu->lock);
+}
+
+static void *vfio_iommu_type1_open(unsigned long arg)
+{
+ struct vfio_iommu *iommu;
+
+ if (arg != VFIO_TYPE1_IOMMU)
+ return ERR_PTR(-EINVAL);
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&iommu->group_list);
+ INIT_LIST_HEAD(&iommu->dma_list);
+ mutex_init(&iommu->lock);
+
+ /*
+ * Wish we didn't have to know about bus_type here.
+ */
+ iommu->domain = iommu_domain_alloc(&pci_bus_type);
+ if (!iommu->domain) {
+ kfree(iommu);
+ return ERR_PTR(-EIO);
+ }
+
+ /*
+ * Wish we could specify required capabilities rather than create
+ * a domain, see what comes out and hope it doesn't change along
+ * the way. Fortunately we know interrupt remapping is global for
+ * our iommus.
+ */
+ if (!allow_unsafe_interrupts &&
+ !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) {
+ pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
+ __func__);
+ iommu_domain_free(iommu->domain);
+ kfree(iommu);
+ return ERR_PTR(-EPERM);
+ }
+
+ return iommu;
+}
+
+static void vfio_iommu_type1_release(void *iommu_data)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group, *group_tmp;
+ struct vfio_dma *dma, *dma_tmp;
+
+ list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) {
+ iommu_detach_group(iommu->domain, group->iommu_group);
+ list_del(&group->next);
+ kfree(group);
+ }
+
+ list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) {
+ vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+ list_del(&dma->next);
+ kfree(dma);
+ }
+
+ iommu_domain_free(iommu->domain);
+ iommu->domain = NULL;
+ kfree(iommu);
+}
+
+static long vfio_iommu_type1_ioctl(void *iommu_data,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ unsigned long minsz;
+
+ if (cmd == VFIO_CHECK_EXTENSION) {
+ switch (arg) {
+ case VFIO_TYPE1_IOMMU:
+ return 1;
+ default:
+ return 0;
+ }
+ } else if (cmd == VFIO_IOMMU_GET_INFO) {
+ struct vfio_iommu_type1_info info;
+
+ minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = 0;
+
+ info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_IOMMU_MAP_DMA) {
+ struct vfio_iommu_type1_dma_map map;
+ uint32_t mask = VFIO_DMA_MAP_FLAG_READ |
+ VFIO_DMA_MAP_FLAG_WRITE;
+
+ minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
+
+ if (copy_from_user(&map, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (map.argsz < minsz || map.flags & ~mask)
+ return -EINVAL;
+
+ return vfio_dma_do_map(iommu, &map);
+
+ } else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
+ struct vfio_iommu_type1_dma_unmap unmap;
+
+ minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
+
+ if (copy_from_user(&unmap, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (unmap.argsz < minsz || unmap.flags)
+ return -EINVAL;
+
+ return vfio_dma_do_unmap(iommu, &unmap);
+ }
+
+ return -ENOTTY;
+}
+
+static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
+ .name = "vfio-iommu-type1",
+ .owner = THIS_MODULE,
+ .open = vfio_iommu_type1_open,
+ .release = vfio_iommu_type1_release,
+ .ioctl = vfio_iommu_type1_ioctl,
+ .attach_group = vfio_iommu_type1_attach_group,
+ .detach_group = vfio_iommu_type1_detach_group,
+};
+
+static int __init vfio_iommu_type1_init(void)
+{
+ if (!iommu_present(&pci_bus_type))
+ return -ENODEV;
+
+ return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+static void __exit vfio_iommu_type1_cleanup(void)
+{
+ vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+module_init(vfio_iommu_type1_init);
+module_exit(vfio_iommu_type1_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index b0b2ac335347..747442d2c0f6 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -90,7 +90,8 @@
#undef DEBUG
#ifdef DEBUG
-#define DBG(fmt, args...) printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
+#define DBG(fmt, args...) \
+ printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
#else
#define DBG(fmt, args...)
#endif
@@ -449,8 +450,9 @@ static int aty128_decode_var(struct fb_var_screeninfo *var,
struct aty128fb_par *par);
#if 0
static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
- void __iomem *bios);
-static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
+ void __iomem *bios);
+static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev,
+ const struct aty128fb_par *par);
#endif
static void aty128_timings(struct aty128fb_par *par);
static void aty128_init_engine(struct aty128fb_par *par);
@@ -779,7 +781,8 @@ static u32 depth_to_dst(u32 depth)
#ifndef __sparc__
-static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
+static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par,
+ struct pci_dev *dev)
{
u16 dptr;
u8 rom_type;
@@ -811,13 +814,14 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
/* Look for the PCI data to check the ROM type */
dptr = BIOS_IN16(0x18);
- /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
- * for now, until I've verified this works everywhere. The goal here is more
- * to phase out Open Firmware images.
+ /* Check the PCI data signature. If it's wrong, we still assume a normal
+ * x86 ROM for now, until I've verified this works everywhere.
+ * The goal here is more to phase out Open Firmware images.
*
- * Currently, we only look at the first PCI data, we could iteratre and deal with
- * them all, and we should use fb_bios_start relative to start of image and not
- * relative start of ROM, but so far, I never found a dual-image ATI card
+ * Currently, we only look at the first PCI data, we could iteratre and
+ * deal with them all, and we should use fb_bios_start relative to start
+ * of image and not relative start of ROM, but so far, I never found a
+ * dual-image ATI card.
*
* typedef struct {
* u32 signature; + 0x00
@@ -852,7 +856,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
goto failed;
default:
- printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type);
+ printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
+ rom_type);
goto failed;
}
anyway:
@@ -863,7 +868,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
return NULL;
}
-static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
+static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
+ unsigned char __iomem *bios)
{
unsigned int bios_hdr;
unsigned int bios_pll;
@@ -1247,10 +1253,13 @@ static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
{
if (on) {
- aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
- aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
+ CRT_CRTC_ON);
+ aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE2_SNOOP_EN));
} else
- aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
+ ~CRT_CRTC_ON);
}
static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
@@ -1281,7 +1290,8 @@ static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
}
}
-static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par)
+static void aty128_set_pll(struct aty128_pll *pll,
+ const struct aty128fb_par *par)
{
u32 div3;
@@ -1366,7 +1376,8 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
}
-static int aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var)
+static int aty128_pll_to_var(const struct aty128_pll *pll,
+ struct fb_var_screeninfo *var)
{
var->pixclock = 100000000 / pll->vclk;
@@ -1512,7 +1523,8 @@ static int aty128fb_set_par(struct fb_info *info)
* encode/decode the User Defined Part of the Display
*/
-static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par)
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par)
{
int err;
struct aty128_crtc crtc;
@@ -1559,7 +1571,8 @@ static int aty128_encode_var(struct fb_var_screeninfo *var,
}
-static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct aty128fb_par par;
int err;
@@ -1575,7 +1588,8 @@ static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *inf
/*
* Pan or Wrap the Display
*/
-static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb)
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
{
struct aty128fb_par *par = fb->par;
u32 xoffset, yoffset;
@@ -1594,7 +1608,8 @@ static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *f
par->crtc.xoffset = xoffset;
par->crtc.yoffset = yoffset;
- offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+ offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
+ & ~7;
if (par->crtc.bpp == 24)
offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
@@ -1620,11 +1635,13 @@ static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
* do mirroring
*/
- aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE_ACCESS_CNTL);
aty_st_8(PALETTE_INDEX, regno);
aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
#endif
- aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
+ ~DAC_PALETTE_ACCESS_CNTL);
}
aty_st_8(PALETTE_INDEX, regno);
@@ -1753,7 +1770,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
aty_st_le32(LVDS_GEN_CNTL, reg);
}
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
- reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT);
+ reg |= (aty128_bl_get_level_brightness(par, level) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_ON | LVDS_EN;
reg &= ~LVDS_DISPLAY_DIS;
@@ -1764,7 +1782,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
#endif
} else {
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
- reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT);
+ reg |= (aty128_bl_get_level_brightness(par, 0) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_DISPLAY_DIS;
aty_st_le32(LVDS_GEN_CNTL, reg);
@@ -1869,7 +1888,8 @@ static void aty128_early_resume(void *data)
}
#endif /* CONFIG_PPC_PMAC */
-static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct aty128fb_par *par = info->par;
@@ -1887,7 +1907,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
/* range check to make sure */
if (ent->driver_data < ARRAY_SIZE(r128_family))
- strlcat(video_card, r128_family[ent->driver_data], sizeof(video_card));
+ strlcat(video_card, r128_family[ent->driver_data],
+ sizeof(video_card));
printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
@@ -1911,11 +1932,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
/* Indicate sleep capability */
if (par->chip_gen == rage_M3) {
pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
-#if 0 /* Disable the early video resume hack for now as it's causing problems, among
- * others we now rely on the PCI core restoring the config space for us, which
- * isn't the case with that hack, and that code path causes various things to
- * be called with interrupts off while they shouldn't. I'm leaving the code in
- * as it can be useful for debugging purposes
+#if 0 /* Disable the early video resume hack for now as it's causing problems,
+ * among others we now rely on the PCI core restoring the config space
+ * for us, which isn't the case with that hack, and that code path causes
+ * various things to be called with interrupts off while they shouldn't.
+ * I'm leaving the code in as it can be useful for debugging purposes
*/
pmac_set_early_video_resume(aty128_early_resume, par);
#endif
@@ -1953,11 +1974,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
default_vmode = VMODE_1152_768_60;
if (default_cmode > 16)
- default_cmode = CMODE_32;
+ default_cmode = CMODE_32;
else if (default_cmode > 8)
- default_cmode = CMODE_16;
+ default_cmode = CMODE_16;
else
- default_cmode = CMODE_8;
+ default_cmode = CMODE_8;
if (mac_vmode_to_var(default_vmode, default_cmode, &var))
var = default_var;
@@ -2018,7 +2039,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
#ifdef CONFIG_PCI
/* register a card ++ajoshi */
-static int __devinit aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
unsigned long fb_addr, reg_addr;
struct aty128fb_par *par;
@@ -2318,39 +2340,39 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
u_int width, u_int height,
struct fb_info_aty128 *par)
{
- u32 save_dp_datatype, save_dp_cntl, dstval;
-
- if (!width || !height)
- return;
-
- dstval = depth_to_dst(par->current_par.crtc.depth);
- if (dstval == DST_24BPP) {
- srcx *= 3;
- dstx *= 3;
- width *= 3;
- } else if (dstval == -EINVAL) {
- printk("aty128fb: invalid depth or RGBA\n");
- return;
- }
-
- wait_for_fifo(2, par);
- save_dp_datatype = aty_ld_le32(DP_DATATYPE);
- save_dp_cntl = aty_ld_le32(DP_CNTL);
-
- wait_for_fifo(6, par);
- aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
- aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
- aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
- aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
-
- aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
- aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
-
- par->blitter_may_be_busy = 1;
-
- wait_for_fifo(2, par);
- aty_st_le32(DP_DATATYPE, save_dp_datatype);
- aty_st_le32(DP_CNTL, save_dp_cntl);
+ u32 save_dp_datatype, save_dp_cntl, dstval;
+
+ if (!width || !height)
+ return;
+
+ dstval = depth_to_dst(par->current_par.crtc.depth);
+ if (dstval == DST_24BPP) {
+ srcx *= 3;
+ dstx *= 3;
+ width *= 3;
+ } else if (dstval == -EINVAL) {
+ printk("aty128fb: invalid depth or RGBA\n");
+ return;
+ }
+
+ wait_for_fifo(2, par);
+ save_dp_datatype = aty_ld_le32(DP_DATATYPE);
+ save_dp_cntl = aty_ld_le32(DP_CNTL);
+
+ wait_for_fifo(6, par);
+ aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
+ aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
+ aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
+
+ aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
+ aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+
+ par->blitter_may_be_busy = 1;
+
+ wait_for_fifo(2, par);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype);
+ aty_st_le32(DP_CNTL, save_dp_cntl);
}
@@ -2358,17 +2380,17 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
* Text mode accelerated functions
*/
-static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx,
- int height, int width)
+static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy,
+ int dx, int height, int width)
{
- sx *= fontwidth(p);
- sy *= fontheight(p);
- dx *= fontwidth(p);
- dy *= fontheight(p);
- width *= fontwidth(p);
- height *= fontheight(p);
-
- aty128_rectcopy(sx, sy, dx, dy, width, height,
+ sx *= fontwidth(p);
+ sy *= fontheight(p);
+ dx *= fontwidth(p);
+ dy *= fontheight(p);
+ width *= fontwidth(p);
+ height *= fontheight(p);
+
+ aty128_rectcopy(sx, sy, dx, dy, width, height,
(struct fb_info_aty128 *)p->fb_info);
}
#endif /* 0 */
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 2979292650d6..cf282763a8dc 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -245,7 +245,7 @@ config BACKLIGHT_CARILLO_RANCH
config BACKLIGHT_PWM
tristate "Generic PWM based Backlight Driver"
- depends on HAVE_PWM
+ depends on PWM
help
If you have a LCD backlight adjustable by PWM, say Y to enable
this driver.
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
index 0443a4f71858..df1cbb7ef6ca 100644
--- a/drivers/video/backlight/atmel-pwm-bl.c
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -127,7 +127,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
struct atmel_pwm_bl *pwmbl;
int retval;
- pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL);
+ pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl),
+ GFP_KERNEL);
if (!pwmbl)
return -ENOMEM;
@@ -154,7 +155,8 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
goto err_free_mem;
if (pwmbl->gpio_on != -1) {
- retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl");
+ retval = devm_gpio_request(&pdev->dev, pwmbl->gpio_on,
+ "gpio_atmel_pwm_bl");
if (retval) {
pwmbl->gpio_on = -1;
goto err_free_pwm;
@@ -164,7 +166,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
retval = gpio_direction_output(pwmbl->gpio_on,
0 ^ pdata->on_active_low);
if (retval)
- goto err_free_gpio;
+ goto err_free_pwm;
}
memset(&props, 0, sizeof(struct backlight_properties));
@@ -174,7 +176,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
&atmel_pwm_bl_ops, &props);
if (IS_ERR(bldev)) {
retval = PTR_ERR(bldev);
- goto err_free_gpio;
+ goto err_free_pwm;
}
pwmbl->bldev = bldev;
@@ -196,13 +198,9 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
err_free_bl_dev:
platform_set_drvdata(pdev, NULL);
backlight_device_unregister(bldev);
-err_free_gpio:
- if (pwmbl->gpio_on != -1)
- gpio_free(pwmbl->gpio_on);
err_free_pwm:
pwm_channel_free(&pwmbl->pwmc);
err_free_mem:
- kfree(pwmbl);
return retval;
}
@@ -210,15 +208,12 @@ static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
{
struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
- if (pwmbl->gpio_on != -1) {
+ if (pwmbl->gpio_on != -1)
gpio_set_value(pwmbl->gpio_on, 0);
- gpio_free(pwmbl->gpio_on);
- }
pwm_channel_disable(&pwmbl->pwmc);
pwm_channel_free(&pwmbl->pwmc);
backlight_device_unregister(pwmbl->bldev);
platform_set_drvdata(pdev, NULL);
- kfree(pwmbl);
return 0;
}
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
index 23d732677ba1..c781768ba892 100644
--- a/drivers/video/backlight/corgi_lcd.c
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -492,7 +492,8 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
lcd->gpio_backlight_cont = -1;
if (gpio_is_valid(pdata->gpio_backlight_on)) {
- err = gpio_request(pdata->gpio_backlight_on, "BL_ON");
+ err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_on,
+ "BL_ON");
if (err) {
dev_err(&spi->dev, "failed to request GPIO%d for "
"backlight_on\n", pdata->gpio_backlight_on);
@@ -504,11 +505,12 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
}
if (gpio_is_valid(pdata->gpio_backlight_cont)) {
- err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT");
+ err = devm_gpio_request(&spi->dev, pdata->gpio_backlight_cont,
+ "BL_CONT");
if (err) {
dev_err(&spi->dev, "failed to request GPIO%d for "
"backlight_cont\n", pdata->gpio_backlight_cont);
- goto err_free_backlight_on;
+ return err;
}
lcd->gpio_backlight_cont = pdata->gpio_backlight_cont;
@@ -525,11 +527,6 @@ static int setup_gpio_backlight(struct corgi_lcd *lcd,
}
}
return 0;
-
-err_free_backlight_on:
- if (gpio_is_valid(lcd->gpio_backlight_on))
- gpio_free(lcd->gpio_backlight_on);
- return err;
}
static int __devinit corgi_lcd_probe(struct spi_device *spi)
@@ -602,12 +599,6 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi)
backlight_update_status(lcd->bl_dev);
backlight_device_unregister(lcd->bl_dev);
- if (gpio_is_valid(lcd->gpio_backlight_on))
- gpio_free(lcd->gpio_backlight_on);
-
- if (gpio_is_valid(lcd->gpio_backlight_cont))
- gpio_free(lcd->gpio_backlight_cont);
-
corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
lcd_device_unregister(lcd->lcd_dev);
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
index 40f606a86093..2d90c0648aa0 100644
--- a/drivers/video/backlight/l4f00242t03.c
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -175,28 +175,27 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
priv->spi = spi;
- ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
- "lcd l4f00242t03 reset");
+ ret = devm_gpio_request_one(&spi->dev, pdata->reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "lcd l4f00242t03 reset");
if (ret) {
dev_err(&spi->dev,
"Unable to get the lcd l4f00242t03 reset gpio.\n");
return ret;
}
- ret = gpio_request_one(pdata->data_enable_gpio, GPIOF_OUT_INIT_LOW,
- "lcd l4f00242t03 data enable");
+ ret = devm_gpio_request_one(&spi->dev, pdata->data_enable_gpio,
+ GPIOF_OUT_INIT_LOW, "lcd l4f00242t03 data enable");
if (ret) {
dev_err(&spi->dev,
"Unable to get the lcd l4f00242t03 data en gpio.\n");
- goto err;
+ return ret;
}
priv->io_reg = regulator_get(&spi->dev, "vdd");
if (IS_ERR(priv->io_reg)) {
- ret = PTR_ERR(priv->io_reg);
dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
__func__);
- goto err2;
+ return PTR_ERR(priv->io_reg);
}
priv->core_reg = regulator_get(&spi->dev, "vcore");
@@ -204,14 +203,14 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
ret = PTR_ERR(priv->core_reg);
dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
__func__);
- goto err3;
+ goto err1;
}
priv->ld = lcd_device_register("l4f00242t03",
&spi->dev, priv, &l4f_ops);
if (IS_ERR(priv->ld)) {
ret = PTR_ERR(priv->ld);
- goto err4;
+ goto err2;
}
/* Init the LCD */
@@ -223,14 +222,10 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
return 0;
-err4:
+err2:
regulator_put(priv->core_reg);
-err3:
+err1:
regulator_put(priv->io_reg);
-err2:
- gpio_free(pdata->data_enable_gpio);
-err:
- gpio_free(pdata->reset_gpio);
return ret;
}
@@ -238,16 +233,12 @@ err:
static int __devexit l4f00242t03_remove(struct spi_device *spi)
{
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev);
- struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data;
l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
lcd_device_unregister(priv->ld);
dev_set_drvdata(&spi->dev, NULL);
- gpio_free(pdata->data_enable_gpio);
- gpio_free(pdata->reset_gpio);
-
regulator_put(priv->io_reg);
regulator_put(priv->core_reg);
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
index bebeb63607db..18dca0c29c68 100644
--- a/drivers/video/backlight/lm3533_bl.c
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -295,7 +295,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev)
return -EINVAL;
}
- bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+ bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL);
if (!bl) {
dev_err(&pdev->dev,
"failed to allocate memory for backlight\n");
@@ -317,8 +317,7 @@ static int __devinit lm3533_bl_probe(struct platform_device *pdev)
&lm3533_bl_ops, &props);
if (IS_ERR(bd)) {
dev_err(&pdev->dev, "failed to register backlight device\n");
- ret = PTR_ERR(bd);
- goto err_free;
+ return PTR_ERR(bd);
}
bl->bd = bd;
@@ -348,8 +347,6 @@ err_sysfs_remove:
sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
err_unregister:
backlight_device_unregister(bd);
-err_free:
- kfree(bl);
return ret;
}
@@ -367,7 +364,6 @@ static int __devexit lm3533_bl_remove(struct platform_device *pdev)
lm3533_ctrlbank_disable(&bl->cb);
sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
backlight_device_unregister(bd);
- kfree(bl);
return 0;
}
diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c
index a9f2c36966f1..ea43f2254196 100644
--- a/drivers/video/backlight/lms283gf05.c
+++ b/drivers/video/backlight/lms283gf05.c
@@ -158,29 +158,27 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
int ret = 0;
if (pdata != NULL) {
- ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET");
+ ret = devm_gpio_request(&spi->dev, pdata->reset_gpio,
+ "LMS285GF05 RESET");
if (ret)
return ret;
ret = gpio_direction_output(pdata->reset_gpio,
!pdata->reset_inverted);
if (ret)
- goto err;
+ return ret;
}
st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
GFP_KERNEL);
if (st == NULL) {
dev_err(&spi->dev, "No memory for device state\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops);
- if (IS_ERR(ld)) {
- ret = PTR_ERR(ld);
- goto err;
- }
+ if (IS_ERR(ld))
+ return PTR_ERR(ld);
st->spi = spi;
st->ld = ld;
@@ -193,24 +191,14 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq));
return 0;
-
-err:
- if (pdata != NULL)
- gpio_free(pdata->reset_gpio);
-
- return ret;
}
static int __devexit lms283gf05_remove(struct spi_device *spi)
{
struct lms283gf05_state *st = dev_get_drvdata(&spi->dev);
- struct lms283gf05_pdata *pdata = st->spi->dev.platform_data;
lcd_device_unregister(st->ld);
- if (pdata != NULL)
- gpio_free(pdata->reset_gpio);
-
return 0;
}
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
index 72a0e0c917cf..aa6d4f71131f 100644
--- a/drivers/video/backlight/lp855x_bl.c
+++ b/drivers/video/backlight/lp855x_bl.c
@@ -14,11 +14,15 @@
#include <linux/i2c.h>
#include <linux/backlight.h>
#include <linux/err.h>
-#include <linux/lp855x.h>
+#include <linux/platform_data/lp855x.h>
/* Registers */
-#define BRIGHTNESS_CTRL (0x00)
-#define DEVICE_CTRL (0x01)
+#define BRIGHTNESS_CTRL 0x00
+#define DEVICE_CTRL 0x01
+#define EEPROM_START 0xA0
+#define EEPROM_END 0xA7
+#define EPROM_START 0xA0
+#define EPROM_END 0xAF
#define BUF_SIZE 20
#define DEFAULT_BL_NAME "lcd-backlight"
diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c
index f519d55a294c..469cf0f109d2 100644
--- a/drivers/video/backlight/ot200_bl.c
+++ b/drivers/video/backlight/ot200_bl.c
@@ -84,7 +84,8 @@ static int ot200_backlight_probe(struct platform_device *pdev)
int retval = 0;
/* request gpio */
- if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) {
+ if (devm_gpio_request(&pdev->dev, GPIO_DIMM,
+ "ot200 backlight dimmer") < 0) {
dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM);
return -ENODEV;
}
@@ -93,14 +94,13 @@ static int ot200_backlight_probe(struct platform_device *pdev)
pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY);
if (!pwm_timer) {
dev_err(&pdev->dev, "MFGPT 7 not available\n");
- retval = -ENODEV;
- goto error_mfgpt_alloc;
+ return -ENODEV;
}
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
retval = -ENOMEM;
- goto error_kzalloc;
+ goto error_devm_kzalloc;
}
/* setup gpio */
@@ -122,26 +122,21 @@ static int ot200_backlight_probe(struct platform_device *pdev)
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
retval = PTR_ERR(bl);
- goto error_backlight_device_register;
+ goto error_devm_kzalloc;
}
platform_set_drvdata(pdev, bl);
return 0;
-error_backlight_device_register:
- kfree(data);
-error_kzalloc:
+error_devm_kzalloc:
cs5535_mfgpt_free_timer(pwm_timer);
-error_mfgpt_alloc:
- gpio_free(GPIO_DIMM);
return retval;
}
static int ot200_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct ot200_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
@@ -152,9 +147,7 @@ static int ot200_backlight_remove(struct platform_device *pdev)
MAX_COMP2 - dim_table[100]);
cs5535_mfgpt_free_timer(pwm_timer);
- gpio_free(GPIO_DIMM);
- kfree(data);
return 0;
}
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 342b7d7cbb63..995f0164c9b0 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -26,11 +26,13 @@ struct pwm_bl_data {
struct device *dev;
unsigned int period;
unsigned int lth_brightness;
+ unsigned int *levels;
int (*notify)(struct device *,
int brightness);
void (*notify_after)(struct device *,
int brightness);
int (*check_fb)(struct device *, struct fb_info *);
+ void (*exit)(struct device *);
};
static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -52,9 +54,18 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
} else {
- brightness = pb->lth_brightness +
- (brightness * (pb->period - pb->lth_brightness) / max);
- pwm_config(pb->pwm, brightness, pb->period);
+ int duty_cycle;
+
+ if (pb->levels) {
+ duty_cycle = pb->levels[brightness];
+ max = pb->levels[max];
+ } else {
+ duty_cycle = brightness;
+ }
+
+ duty_cycle = pb->lth_brightness +
+ (duty_cycle * (pb->period - pb->lth_brightness) / max);
+ pwm_config(pb->pwm, duty_cycle, pb->period);
pwm_enable(pb->pwm);
}
@@ -83,17 +94,98 @@ static const struct backlight_ops pwm_backlight_ops = {
.check_fb = pwm_backlight_check_fb,
};
+#ifdef CONFIG_OF
+static int pwm_backlight_parse_dt(struct device *dev,
+ struct platform_pwm_backlight_data *data)
+{
+ struct device_node *node = dev->of_node;
+ struct property *prop;
+ int length;
+ u32 value;
+ int ret;
+
+ if (!node)
+ return -ENODEV;
+
+ memset(data, 0, sizeof(*data));
+
+ /* determine the number of brightness levels */
+ prop = of_find_property(node, "brightness-levels", &length);
+ if (!prop)
+ return -EINVAL;
+
+ data->max_brightness = length / sizeof(u32);
+
+ /* read brightness levels from DT property */
+ if (data->max_brightness > 0) {
+ size_t size = sizeof(*data->levels) * data->max_brightness;
+
+ data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!data->levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(node, "brightness-levels",
+ data->levels,
+ data->max_brightness);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u32(node, "default-brightness-level",
+ &value);
+ if (ret < 0)
+ return ret;
+
+ if (value >= data->max_brightness) {
+ dev_warn(dev, "invalid default brightness level: %u, using %u\n",
+ value, data->max_brightness - 1);
+ value = data->max_brightness - 1;
+ }
+
+ data->dft_brightness = value;
+ data->max_brightness--;
+ }
+
+ /*
+ * TODO: Most users of this driver use a number of GPIOs to control
+ * backlight power. Support for specifying these needs to be
+ * added.
+ */
+
+ return 0;
+}
+
+static struct of_device_id pwm_backlight_of_match[] = {
+ { .compatible = "pwm-backlight" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
+#else
+static int pwm_backlight_parse_dt(struct device *dev,
+ struct platform_pwm_backlight_data *data)
+{
+ return -ENODEV;
+}
+#endif
+
static int pwm_backlight_probe(struct platform_device *pdev)
{
- struct backlight_properties props;
struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
+ struct platform_pwm_backlight_data defdata;
+ struct backlight_properties props;
struct backlight_device *bl;
struct pwm_bl_data *pb;
+ unsigned int max;
int ret;
if (!data) {
- dev_err(&pdev->dev, "failed to find platform data\n");
- return -EINVAL;
+ ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to find platform data\n");
+ return ret;
+ }
+
+ data = &defdata;
}
if (data->init) {
@@ -109,21 +201,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
goto err_alloc;
}
- pb->period = data->pwm_period_ns;
+ if (data->levels) {
+ max = data->levels[data->max_brightness];
+ pb->levels = data->levels;
+ } else
+ max = data->max_brightness;
+
pb->notify = data->notify;
pb->notify_after = data->notify_after;
pb->check_fb = data->check_fb;
- pb->lth_brightness = data->lth_brightness *
- (data->pwm_period_ns / data->max_brightness);
+ pb->exit = data->exit;
pb->dev = &pdev->dev;
- pb->pwm = pwm_request(data->pwm_id, "backlight");
+ pb->pwm = pwm_get(&pdev->dev, NULL);
if (IS_ERR(pb->pwm)) {
- dev_err(&pdev->dev, "unable to request PWM for backlight\n");
- ret = PTR_ERR(pb->pwm);
- goto err_alloc;
- } else
- dev_dbg(&pdev->dev, "got pwm for backlight\n");
+ dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
+
+ pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+ if (IS_ERR(pb->pwm)) {
+ dev_err(&pdev->dev, "unable to request legacy PWM\n");
+ ret = PTR_ERR(pb->pwm);
+ goto err_alloc;
+ }
+ }
+
+ dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+ /*
+ * The DT case will set the pwm_period_ns field to 0 and store the
+ * period, parsed from the DT, in the PWM device. For the non-DT case,
+ * set the period from platform data.
+ */
+ if (data->pwm_period_ns > 0)
+ pwm_set_period(pb->pwm, data->pwm_period_ns);
+
+ pb->period = pwm_get_period(pb->pwm);
+ pb->lth_brightness = data->lth_brightness * (pb->period / max);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
@@ -143,7 +256,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
return 0;
err_bl:
- pwm_free(pb->pwm);
+ pwm_put(pb->pwm);
err_alloc:
if (data->exit)
data->exit(&pdev->dev);
@@ -152,16 +265,15 @@ err_alloc:
static int pwm_backlight_remove(struct platform_device *pdev)
{
- struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
struct backlight_device *bl = platform_get_drvdata(pdev);
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
backlight_device_unregister(bl);
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
- pwm_free(pb->pwm);
- if (data->exit)
- data->exit(&pdev->dev);
+ pwm_put(pb->pwm);
+ if (pb->exit)
+ pb->exit(&pdev->dev);
return 0;
}
@@ -195,11 +307,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
static struct platform_driver pwm_backlight_driver = {
.driver = {
- .name = "pwm-backlight",
- .owner = THIS_MODULE,
+ .name = "pwm-backlight",
+ .owner = THIS_MODULE,
#ifdef CONFIG_PM
- .pm = &pwm_backlight_pm_ops,
+ .pm = &pwm_backlight_pm_ops,
#endif
+ .of_match_table = of_match_ptr(pwm_backlight_of_match),
},
.probe = pwm_backlight_probe,
.remove = pwm_backlight_remove,
diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
index 0d54e607e82d..49342e1d20be 100644
--- a/drivers/video/backlight/tosa_bl.c
+++ b/drivers/video/backlight/tosa_bl.c
@@ -92,14 +92,14 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj;
- ret = gpio_request(TOSA_GPIO_BL_C20MA, "backlight");
+ ret = devm_gpio_request(&client->dev, TOSA_GPIO_BL_C20MA, "backlight");
if (ret) {
dev_dbg(&data->bl->dev, "Unable to request gpio!\n");
return ret;
}
ret = gpio_direction_output(TOSA_GPIO_BL_C20MA, 0);
if (ret)
- goto err_gpio_dir;
+ return ret;
i2c_set_clientdata(client, data);
data->i2c = client;
@@ -123,8 +123,6 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
err_reg:
data->bl = NULL;
-err_gpio_dir:
- gpio_free(TOSA_GPIO_BL_C20MA);
return ret;
}
@@ -135,8 +133,6 @@ static int __devexit tosa_bl_remove(struct i2c_client *client)
backlight_device_unregister(data->bl);
data->bl = NULL;
- gpio_free(TOSA_GPIO_BL_C20MA);
-
return 0;
}
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
index 47823b8efff0..33047a66cc24 100644
--- a/drivers/video/backlight/tosa_lcd.c
+++ b/drivers/video/backlight/tosa_lcd.c
@@ -193,7 +193,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
data->spi = spi;
dev_set_drvdata(&spi->dev, data);
- ret = gpio_request(TOSA_GPIO_TG_ON, "tg #pwr");
+ ret = devm_gpio_request(&spi->dev, TOSA_GPIO_TG_ON, "tg #pwr");
if (ret < 0)
goto err_gpio_tg;
@@ -201,7 +201,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
ret = gpio_direction_output(TOSA_GPIO_TG_ON, 0);
if (ret < 0)
- goto err_gpio_dir;
+ goto err_gpio_tg;
mdelay(60);
tosa_lcd_tg_init(data);
@@ -221,8 +221,6 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
err_register:
tosa_lcd_tg_off(data);
-err_gpio_dir:
- gpio_free(TOSA_GPIO_TG_ON);
err_gpio_tg:
dev_set_drvdata(&spi->dev, NULL);
return ret;
@@ -239,7 +237,6 @@ static int __devexit tosa_lcd_remove(struct spi_device *spi)
tosa_lcd_tg_off(data);
- gpio_free(TOSA_GPIO_TG_ON);
dev_set_drvdata(&spi->dev, NULL);
return 0;
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 47118c75a4c0..7ae9d53f2bf1 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -30,7 +30,10 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/console.h>
+#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/lcm.h>
#include <video/da8xx-fb.h>
#include <asm/div64.h>
@@ -160,6 +163,13 @@ struct da8xx_fb_par {
wait_queue_head_t vsync_wait;
int vsync_flag;
int vsync_timeout;
+ spinlock_t lock_for_chan_update;
+
+ /*
+ * LCDC has 2 ping pong DMA channels, channel 0
+ * and channel 1.
+ */
+ unsigned int which_dma_channel_done;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
unsigned int lcd_fck_rate;
@@ -260,10 +270,18 @@ static inline void lcd_enable_raster(void)
{
u32 reg;
+ /* Put LCDC in reset for several cycles */
+ if (lcd_revision == LCD_VERSION_2)
+ /* Write 1 to reset LCDC */
+ lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
+ mdelay(1);
+
/* Bring LCDC out of reset */
if (lcd_revision == LCD_VERSION_2)
lcdc_write(0, LCD_CLK_RESET_REG);
+ mdelay(1);
+ /* Above reset sequence doesnot reset register context */
reg = lcdc_read(LCD_RASTER_CTRL_REG);
if (!(reg & LCD_RASTER_ENABLE))
lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
@@ -277,10 +295,6 @@ static inline void lcd_disable_raster(void)
reg = lcdc_read(LCD_RASTER_CTRL_REG);
if (reg & LCD_RASTER_ENABLE)
lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
-
- if (lcd_revision == LCD_VERSION_2)
- /* Write 1 to reset LCDC */
- lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
}
static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
@@ -344,8 +358,8 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
lcd_enable_raster();
}
-/* Configure the Burst Size of DMA */
-static int lcd_cfg_dma(int burst_size)
+/* Configure the Burst Size and fifo threhold of DMA */
+static int lcd_cfg_dma(int burst_size, int fifo_th)
{
u32 reg;
@@ -369,6 +383,9 @@ static int lcd_cfg_dma(int burst_size)
default:
return -EINVAL;
}
+
+ reg |= (fifo_th << 8);
+
lcdc_write(reg, LCD_DMA_CTRL_REG);
return 0;
@@ -670,8 +687,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) &
~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
- /* Configure the DMA burst size. */
- ret = lcd_cfg_dma(cfg->dma_burst_sz);
+ /* Configure the DMA burst size and fifo threshold. */
+ ret = lcd_cfg_dma(cfg->dma_burst_sz, cfg->fifo_th);
if (ret < 0)
return ret;
@@ -715,7 +732,6 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
{
struct da8xx_fb_par *par = arg;
u32 stat = lcdc_read(LCD_MASKED_STAT_REG);
- u32 reg_int;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
lcd_disable_raster();
@@ -732,10 +748,8 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
lcdc_write(stat, LCD_MASKED_STAT_REG);
- /* Disable PL completion inerrupt */
- reg_int = lcdc_read(LCD_INT_ENABLE_CLR_REG) |
- (LCD_V2_PL_INT_ENA);
- lcdc_write(reg_int, LCD_INT_ENABLE_CLR_REG);
+ /* Disable PL completion interrupt */
+ lcdc_write(LCD_V2_PL_INT_ENA, LCD_INT_ENABLE_CLR_REG);
/* Setup and start data loading mode */
lcd_blit(LOAD_DATA, par);
@@ -743,6 +757,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
lcdc_write(stat, LCD_MASKED_STAT_REG);
if (stat & LCD_END_OF_FRAME0) {
+ par->which_dma_channel_done = 0;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
@@ -752,6 +767,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
}
if (stat & LCD_END_OF_FRAME1) {
+ par->which_dma_channel_done = 1;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
@@ -798,6 +814,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
lcdc_write(stat, LCD_STAT_REG);
if (stat & LCD_END_OF_FRAME0) {
+ par->which_dma_channel_done = 0;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
@@ -807,6 +824,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
}
if (stat & LCD_END_OF_FRAME1) {
+ par->which_dma_channel_done = 1;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
@@ -1021,11 +1039,14 @@ static int cfb_blank(int blank, struct fb_info *info)
par->blank = blank;
switch (blank) {
case FB_BLANK_UNBLANK:
+ lcd_enable_raster();
+
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
-
- lcd_enable_raster();
break;
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
if (par->panel_power_ctrl)
par->panel_power_ctrl(0);
@@ -1052,6 +1073,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix = &fbi->fix;
unsigned int end;
unsigned int start;
+ unsigned long irq_flags;
if (var->xoffset != fbi->var.xoffset ||
var->yoffset != fbi->var.yoffset) {
@@ -1069,6 +1091,21 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
end = start + fbi->var.yres * fix->line_length - 1;
par->dma_start = start;
par->dma_end = end;
+ spin_lock_irqsave(&par->lock_for_chan_update,
+ irq_flags);
+ if (par->which_dma_channel_done == 0) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+ } else if (par->which_dma_channel_done == 1) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+ }
+ spin_unlock_irqrestore(&par->lock_for_chan_update,
+ irq_flags);
}
}
@@ -1114,6 +1151,7 @@ static int __devinit fb_probe(struct platform_device *device)
struct da8xx_fb_par *par;
resource_size_t len;
int ret, i;
+ unsigned long ulcm;
if (fb_pdata == NULL) {
dev_err(&device->dev, "Can not get platform data\n");
@@ -1209,7 +1247,8 @@ static int __devinit fb_probe(struct platform_device *device)
/* allocate frame buffer */
par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp;
- par->vram_size = PAGE_ALIGN(par->vram_size/8);
+ ulcm = lcm((lcdc_info->width * lcd_cfg->bpp)/8, PAGE_SIZE);
+ par->vram_size = roundup(par->vram_size/8, ulcm);
par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
par->vram_virt = dma_alloc_coherent(NULL,
@@ -1296,6 +1335,8 @@ static int __devinit fb_probe(struct platform_device *device)
/* initialize the vsync wait queue */
init_waitqueue_head(&par->vsync_wait);
par->vsync_timeout = HZ / 5;
+ par->which_dma_channel_done = -1;
+ spin_lock_init(&par->lock_for_chan_update);
/* Register the Frame Buffer */
if (register_framebuffer(da8xx_fb_info) < 0) {
@@ -1382,11 +1423,12 @@ static int fb_resume(struct platform_device *dev)
struct da8xx_fb_par *par = info->par;
console_lock();
+ clk_enable(par->lcdc_clk);
+ lcd_enable_raster();
+
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
- clk_enable(par->lcdc_clk);
- lcd_enable_raster();
fb_set_suspend(info, 0);
console_unlock();
diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c
index a268cbf1cbea..68b9b511ce80 100644
--- a/drivers/video/epson1355fb.c
+++ b/drivers/video/epson1355fb.c
@@ -477,11 +477,11 @@ static __init unsigned int get_fb_size(struct fb_info *info)
return size;
}
-static int epson1355_width_tab[2][4] __initdata =
+static int epson1355_width_tab[2][4] __devinitdata =
{ {4, 8, 16, -1}, {9, 12, 16, -1} };
-static int epson1355_bpp_tab[8] __initdata = { 1, 2, 4, 8, 15, 16 };
+static int epson1355_bpp_tab[8] __devinitdata = { 1, 2, 4, 8, 15, 16 };
-static void __init fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
+static void __devinit fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
{
struct fb_var_screeninfo *var = &info->var;
struct fb_fix_screeninfo *fix = &info->fix;
@@ -601,7 +601,7 @@ static int epson1355fb_remove(struct platform_device *dev)
return 0;
}
-int __devinit epson1355fb_probe(struct platform_device *dev)
+static int __devinit epson1355fb_probe(struct platform_device *dev)
{
struct epson1355_par *default_par;
struct fb_info *info;
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c
index a36b2d28280e..c6c016a506ce 100644
--- a/drivers/video/exynos/exynos_dp_core.c
+++ b/drivers/video/exynos/exynos_dp_core.c
@@ -47,7 +47,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
exynos_dp_init_hpd(dp);
- udelay(200);
+ usleep_range(200, 210);
while (exynos_dp_get_plug_in_status(dp) != 0) {
timeout_loop++;
@@ -55,7 +55,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
dev_err(dp->dev, "failed to get hpd plug status\n");
return -ETIMEDOUT;
}
- udelay(10);
+ usleep_range(10, 11);
}
return 0;
@@ -304,7 +304,7 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp)
buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
+ DPCD_ADDR_TRAINING_LANE0_SET,
lane_count, buf);
}
@@ -336,7 +336,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count)
u8 lane_status;
lane_align = link_status[2];
- if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0)
+ if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
return -EINVAL;
for (lane = 0; lane < lane_count; lane++) {
@@ -407,6 +407,9 @@ static unsigned int exynos_dp_get_lane_link_training(
case 3:
reg = exynos_dp_get_lane3_link_training(dp);
break;
+ default:
+ WARN_ON(1);
+ return 0;
}
return reg;
@@ -483,7 +486,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
u8 pre_emphasis;
u8 training_lane;
- udelay(100);
+ usleep_range(100, 101);
exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
6, link_status);
@@ -501,7 +504,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
buf[0] = DPCD_SCRAMBLING_DISABLED |
DPCD_TRAINING_PATTERN_2;
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
buf[0]);
for (lane = 0; lane < lane_count; lane++) {
@@ -568,7 +571,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
u8 adjust_request[2];
- udelay(400);
+ usleep_range(400, 401);
exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
6, link_status);
@@ -736,7 +739,7 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
if (retval == 0)
break;
- udelay(100);
+ usleep_range(100, 110);
}
return retval;
@@ -770,7 +773,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
return -ETIMEDOUT;
}
- udelay(1);
+ usleep_range(1, 2);
}
/* Set to use the register calculated M/N video */
@@ -804,7 +807,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
return -ETIMEDOUT;
}
- mdelay(1);
+ usleep_range(1000, 1001);
}
if (retval != 0)
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h
index 1e0f998e0c9f..8526e548c385 100644
--- a/drivers/video/exynos/exynos_dp_core.h
+++ b/drivers/video/exynos/exynos_dp_core.h
@@ -85,10 +85,6 @@ void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
enum pattern_set pattern);
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c
index bcb0e3ae1e9d..2db5b9aa250a 100644
--- a/drivers/video/exynos/exynos_dp_reg.c
+++ b/drivers/video/exynos/exynos_dp_reg.c
@@ -122,7 +122,7 @@ void exynos_dp_reset(struct exynos_dp_device *dp)
LS_CLK_DOMAIN_FUNC_EN_N;
writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
- udelay(20);
+ usleep_range(20, 30);
exynos_dp_lane_swap(dp, 0);
@@ -988,7 +988,7 @@ void exynos_dp_reset_macro(struct exynos_dp_device *dp)
writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
/* 10 us is the minimum reset time. */
- udelay(10);
+ usleep_range(10, 20);
reg &= ~MACRO_RST;
writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
index 9908e75ae761..4bc2b8a5dd8b 100644
--- a/drivers/video/exynos/exynos_mipi_dsi.c
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -154,7 +154,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
if (client_drv && client_drv->power_on)
client_drv->power_on(client_dev, 1);
- exynos_mipi_regulator_disable(dsim);
+ exynos_mipi_regulator_enable(dsim);
/* enable MIPI-DSI PHY. */
if (dsim->pd->phy_enable)
diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h
deleted file mode 100644
index 1f1b270484b0..000000000000
--- a/drivers/video/exynos/s6e8ax0.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* linux/drivers/video/backlight/s6e8ax0.h
- *
- * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions.
- *
- * Copyright (c) 2011 Samsung Electronics
- *
- * Inki Dae, <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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.
-*/
-
-#ifndef _S6E8AX0_H
-#define _S6E8AX0_H
-
-extern void s6e8ax0_init(void);
-
-#endif
-
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 1ddeb11659d4..64cda560c488 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -104,6 +104,8 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
deferred framebuffer IO. then if userspace touches a page
again, we repeat the same scheme */
+ file_update_time(vma->vm_file);
+
/* protect against the workqueue changing the page list */
mutex_lock(&fbdefio->lock);
diff --git a/drivers/video/fb_draw.h b/drivers/video/fb_draw.h
index 04c01faaf772..624ee115f129 100644
--- a/drivers/video/fb_draw.h
+++ b/drivers/video/fb_draw.h
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <linux/fb.h>
+#include <linux/bug.h>
/*
* Compose two values, using a bitmask as decision value
@@ -41,7 +42,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 32:
return 0x0000000100000001ul*pixel;
default:
- panic("pixel_to_pat(): unsupported pixelformat\n");
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
}
}
#else
@@ -66,7 +68,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 32:
return 0x00000001ul*pixel;
default:
- panic("pixel_to_pat(): unsupported pixelformat\n");
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
}
}
#endif
diff --git a/drivers/video/grvga.c b/drivers/video/grvga.c
index da066c210923..5245f9a71892 100644
--- a/drivers/video/grvga.c
+++ b/drivers/video/grvga.c
@@ -354,7 +354,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
*/
if (fb_get_options("grvga", &options)) {
retval = -ENODEV;
- goto err;
+ goto free_fb;
}
if (!options || !*options)
@@ -370,7 +370,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (grvga_parse_custom(this_opt, &info->var) < 0) {
dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt);
retval = -EINVAL;
- goto err1;
+ goto free_fb;
}
} else if (!strncmp(this_opt, "addr", 4))
grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
@@ -387,10 +387,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
info->fix.smem_len = grvga_mem_size;
- if (!request_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
+ if (!devm_request_mem_region(&dev->dev, dev->resource[0].start,
+ resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
dev_err(&dev->dev, "registers already mapped\n");
retval = -EBUSY;
- goto err;
+ goto free_fb;
}
par->regs = of_ioremap(&dev->resource[0], 0,
@@ -400,14 +401,14 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (!par->regs) {
dev_err(&dev->dev, "failed to map registers\n");
retval = -ENOMEM;
- goto err1;
+ goto free_fb;
}
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0) {
dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");
retval = -ENOMEM;
- goto err2;
+ goto unmap_regs;
}
if (mode_opt) {
@@ -415,7 +416,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
if (!retval || retval == 4) {
retval = -EINVAL;
- goto err3;
+ goto dealloc_cmap;
}
}
@@ -427,10 +428,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
physical_start = grvga_fix_addr;
- if (!request_mem_region(physical_start, grvga_mem_size, dev->name)) {
+ if (!devm_request_mem_region(&dev->dev, physical_start,
+ grvga_mem_size, dev->name)) {
dev_err(&dev->dev, "failed to request memory region\n");
retval = -ENOMEM;
- goto err3;
+ goto dealloc_cmap;
}
virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
@@ -438,7 +440,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (!virtual_start) {
dev_err(&dev->dev, "error mapping framebuffer memory\n");
retval = -ENOMEM;
- goto err4;
+ goto dealloc_cmap;
}
} else { /* Allocate frambuffer memory */
@@ -451,7 +453,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
"unable to allocate framebuffer memory (%lu bytes)\n",
grvga_mem_size);
retval = -ENOMEM;
- goto err3;
+ goto dealloc_cmap;
}
physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
@@ -484,7 +486,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
retval = register_framebuffer(info);
if (retval < 0) {
dev_err(&dev->dev, "failed to register framebuffer\n");
- goto err4;
+ goto free_mem;
}
__raw_writel(physical_start, &par->regs->fb_pos);
@@ -493,21 +495,18 @@ static int __devinit grvga_probe(struct platform_device *dev)
return 0;
-err4:
+free_mem:
dev_set_drvdata(&dev->dev, NULL);
- if (grvga_fix_addr) {
- release_mem_region(physical_start, grvga_mem_size);
+ if (grvga_fix_addr)
iounmap((void *)virtual_start);
- } else
+ else
kfree((void *)virtual_start);
-err3:
+dealloc_cmap:
fb_dealloc_cmap(&info->cmap);
-err2:
+unmap_regs:
of_iounmap(&dev->resource[0], par->regs,
resource_size(&dev->resource[0]));
-err1:
- release_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]));
-err:
+free_fb:
framebuffer_release(info);
return retval;
@@ -524,12 +523,10 @@ static int __devexit grvga_remove(struct platform_device *device)
of_iounmap(&device->resource[0], par->regs,
resource_size(&device->resource[0]));
- release_mem_region(device->resource[0].start, resource_size(&device->resource[0]));
- if (!par->fb_alloced) {
- release_mem_region(info->fix.smem_start, info->fix.smem_len);
+ if (!par->fb_alloced)
iounmap(info->screen_base);
- } else
+ else
kfree((void *)info->screen_base);
framebuffer_release(info);
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index eec0d7b748eb..c89f8a8d36d2 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -269,7 +269,7 @@ struct mx3fb_info {
dma_cookie_t cookie;
struct scatterlist sg[2];
- u32 sync; /* preserve var->sync flags */
+ struct fb_var_screeninfo cur_var; /* current var info */
};
static void mx3fb_dma_done(void *);
@@ -698,9 +698,29 @@ static void mx3fb_dma_done(void *arg)
complete(&mx3_fbi->flip_cmpl);
}
+static bool mx3fb_must_set_par(struct fb_info *fbi)
+{
+ struct mx3fb_info *mx3_fbi = fbi->par;
+ struct fb_var_screeninfo old_var = mx3_fbi->cur_var;
+ struct fb_var_screeninfo new_var = fbi->var;
+
+ if ((fbi->var.activate & FB_ACTIVATE_FORCE) &&
+ (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+ return true;
+
+ /*
+ * Ignore xoffset and yoffset update,
+ * because pan display handles this case.
+ */
+ old_var.xoffset = new_var.xoffset;
+ old_var.yoffset = new_var.yoffset;
+
+ return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo));
+}
+
static int __set_par(struct fb_info *fbi, bool lock)
{
- u32 mem_len;
+ u32 mem_len, cur_xoffset, cur_yoffset;
struct ipu_di_signal_cfg sig_cfg;
enum ipu_panel mode = IPU_PANEL_TFT;
struct mx3fb_info *mx3_fbi = fbi->par;
@@ -780,8 +800,25 @@ static int __set_par(struct fb_info *fbi, bool lock)
video->out_height = fbi->var.yres;
video->out_stride = fbi->var.xres_virtual;
- if (mx3_fbi->blank == FB_BLANK_UNBLANK)
+ if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
sdc_enable_channel(mx3_fbi);
+ /*
+ * sg[0] points to fb smem_start address
+ * and is actually active in controller.
+ */
+ mx3_fbi->cur_var.xoffset = 0;
+ mx3_fbi->cur_var.yoffset = 0;
+ }
+
+ /*
+ * Preserve xoffset and yoffest in case they are
+ * inactive in controller as fb is blanked.
+ */
+ cur_xoffset = mx3_fbi->cur_var.xoffset;
+ cur_yoffset = mx3_fbi->cur_var.yoffset;
+ mx3_fbi->cur_var = fbi->var;
+ mx3_fbi->cur_var.xoffset = cur_xoffset;
+ mx3_fbi->cur_var.yoffset = cur_yoffset;
return 0;
}
@@ -802,7 +839,7 @@ static int mx3fb_set_par(struct fb_info *fbi)
mutex_lock(&mx3_fbi->mutex);
- ret = __set_par(fbi, true);
+ ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0;
mutex_unlock(&mx3_fbi->mutex);
@@ -901,8 +938,8 @@ static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
var->grayscale = 0;
/* Preserve sync flags */
- var->sync |= mx3_fbi->sync;
- mx3_fbi->sync |= var->sync;
+ var->sync |= mx3_fbi->cur_var.sync;
+ mx3_fbi->cur_var.sync |= var->sync;
return 0;
}
@@ -1043,8 +1080,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
return -EINVAL;
}
- if (fbi->var.xoffset == var->xoffset &&
- fbi->var.yoffset == var->yoffset)
+ if (mx3_fbi->cur_var.xoffset == var->xoffset &&
+ mx3_fbi->cur_var.yoffset == var->yoffset)
return 0; /* No change, do nothing */
y_bottom = var->yoffset;
@@ -1127,6 +1164,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
else
fbi->var.vmode &= ~FB_VMODE_YWRAP;
+ mx3_fbi->cur_var = fbi->var;
+
mutex_unlock(&mx3_fbi->mutex);
dev_dbg(fbi->device, "Update complete\n");
diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c
index ad741c3d1ae1..eaeed4340e04 100644
--- a/drivers/video/omap2/displays/panel-acx565akm.c
+++ b/drivers/video/omap2/displays/panel-acx565akm.c
@@ -487,6 +487,13 @@ static struct omap_video_timings acx_panel_timings = {
.vfp = 3,
.vsw = 3,
.vbp = 4,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int acx_panel_probe(struct omap_dss_device *dssdev)
@@ -498,8 +505,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
struct backlight_properties props;
dev_dbg(&dssdev->dev, "%s\n", __func__);
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
+
/* FIXME AC bias ? */
dssdev->panel.timings = acx_panel_timings;
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index e42f9dc22123..bc5af2500eb9 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -40,12 +40,6 @@
struct panel_config {
struct omap_video_timings timings;
- int acbi; /* ac-bias pin transitions per interrupt */
- /* Unit: line clocks */
- int acb; /* ac-bias pin frequency */
-
- enum omap_panel_config config;
-
int power_on_delay;
int power_off_delay;
@@ -73,11 +67,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 11,
.vfp = 3,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_lq",
@@ -98,11 +94,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x28,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_ls",
@@ -123,12 +121,13 @@ static struct panel_config generic_dpi_panels[] = {
.vfp = 4,
.vsw = 2,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
- OMAP_DSS_LCD_ONOFF,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "toppoly_tdo35s",
@@ -149,11 +148,13 @@ static struct panel_config generic_dpi_panels[] = {
.vfp = 4,
.vsw = 10,
.vbp = 12 - 10,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "samsung_lte430wq_f0c",
@@ -174,11 +175,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 4,
.vbp = 11,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "seiko_70wvw1tz3",
@@ -199,11 +202,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 2,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "powertip_ph480272t",
@@ -224,11 +229,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 3,
.vfp = 12,
.vbp = 25,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x28,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "innolux_at070tn83",
@@ -249,9 +256,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 2,
.vbp = 7,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.name = "nec_nl2432dr22-11b",
},
@@ -270,9 +281,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
- },
- .config = OMAP_DSS_LCD_TFT,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "h4",
},
@@ -291,10 +306,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 2,
.vbp = 2,
- },
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "apollon",
},
/* FocalTech ETM070003DH6 */
@@ -312,9 +330,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 3,
.vfp = 13,
.vbp = 29,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.name = "focaltech_etm070003dh6",
},
@@ -333,11 +355,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 23,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "microtips_umsh_8173md",
@@ -358,9 +382,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 4,
.vbp = 2,
- },
- .config = OMAP_DSS_LCD_TFT,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "ortustech_com43h4m10xtc",
},
@@ -379,11 +407,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 12,
.vbp = 23,
- },
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "innolux_at080tn52",
},
@@ -401,8 +431,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 26,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT,
.name = "mitsubishi_aa084sb01",
},
/* EDT ET0500G0DH6 */
@@ -419,8 +454,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 35,
.vbp = 10,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT,
.name = "edt_et0500g0dh6",
},
@@ -439,9 +479,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 10,
.vbp = 33,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pd050vl1",
},
@@ -460,9 +504,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 10,
.vbp = 33,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pm070wl4",
},
@@ -481,9 +529,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 4,
.vfp = 1,
.vbp = 23,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pd104slf",
},
};
@@ -573,10 +625,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
if (!panel_config)
return -EINVAL;
- dssdev->panel.config = panel_config->config;
dssdev->panel.timings = panel_config->timings;
- dssdev->panel.acb = panel_config->acb;
- dssdev->panel.acbi = panel_config->acbi;
drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
if (!drv_data)
diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
index 0841cc2b3f77..802807798846 100644
--- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
+++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
@@ -40,6 +40,12 @@ static struct omap_video_timings lb035q02_timings = {
.vsw = 2,
.vfp = 4,
.vbp = 18,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
@@ -82,8 +88,6 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
struct lb035q02_data *ld;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
dssdev->panel.timings = lb035q02_timings;
ld = kzalloc(sizeof(*ld), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c
index 4a34cdc1371b..e6c115373c00 100644
--- a/drivers/video/omap2/displays/panel-n8x0.c
+++ b/drivers/video/omap2/displays/panel-n8x0.c
@@ -473,7 +473,6 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
mutex_init(&ddata->lock);
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings.x_res = 800;
dssdev->panel.timings.y_res = 480;
dssdev->ctrl.pixel_size = 16;
diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
index 8b38b39213f4..b122b0f31c43 100644
--- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
+++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
@@ -76,6 +76,12 @@ static struct omap_video_timings nec_8048_panel_timings = {
.vfp = 3,
.vsw = 1,
.vbp = 4,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
};
static int nec_8048_bl_update_status(struct backlight_device *bl)
@@ -116,9 +122,6 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
struct backlight_properties props;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF |
- OMAP_DSS_LCD_ONOFF;
dssdev->panel.timings = nec_8048_panel_timings;
necd = kzalloc(sizeof(*necd), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c
index 98ebdaddab5a..2d35bd388860 100644
--- a/drivers/video/omap2/displays/panel-picodlp.c
+++ b/drivers/video/omap2/displays/panel-picodlp.c
@@ -69,6 +69,12 @@ static struct omap_video_timings pico_ls_timings = {
.vsw = 2,
.vfp = 3,
.vbp = 14,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
static inline struct picodlp_panel_data
@@ -414,9 +420,6 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
struct i2c_client *picodlp_i2c_client;
int r = 0, picodlp_adapter_id;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS;
- dssdev->panel.acb = 0x0;
dssdev->panel.timings = pico_ls_timings;
picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
index ba38b3ad17d6..bd86ba9ccf76 100644
--- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
@@ -44,6 +44,12 @@ static struct omap_video_timings sharp_ls_timings = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int sharp_ls_bl_update_status(struct backlight_device *bl)
@@ -86,9 +92,6 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
struct sharp_data *sd;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
- dssdev->panel.acb = 0x28;
dssdev->panel.timings = sharp_ls_timings;
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 901576eb5a84..3f5acc7771da 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -882,7 +882,6 @@ static int taal_probe(struct omap_dss_device *dssdev)
goto err;
}
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings = panel_config->timings;
dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c
index bff306e041ca..40cc0cfa5d17 100644
--- a/drivers/video/omap2/displays/panel-tfp410.c
+++ b/drivers/video/omap2/displays/panel-tfp410.c
@@ -39,6 +39,12 @@ static const struct omap_video_timings tfp410_default_timings = {
.vfp = 3,
.vsw = 4,
.vbp = 7,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
struct panel_drv_data {
@@ -95,7 +101,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
return -ENOMEM;
dssdev->panel.timings = tfp410_default_timings;
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
ddata->dssdev = dssdev;
mutex_init(&ddata->lock);
diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index 4b6448b3c31f..fa7baa650ae0 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -267,6 +267,12 @@ static const struct omap_video_timings tpo_td043_timings = {
.vsw = 1,
.vfp = 39,
.vbp = 34,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
@@ -423,8 +429,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
return -ENODEV;
}
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS |
- OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC;
dssdev->panel.timings = tpo_td043_timings;
dssdev->ctrl.pixel_size = 24;
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 43324e5ed25f..b337a8469fd8 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -52,7 +52,7 @@ config OMAP2_DSS_RFBI
DBI is a bus between the host processor and a peripheral,
such as a display or a framebuffer chip.
- See http://www.mipi.org/ for DBI spesifications.
+ See http://www.mipi.org/ for DBI specifications.
config OMAP2_DSS_VENC
bool "VENC support"
@@ -92,7 +92,7 @@ config OMAP2_DSS_DSI
DSI is a high speed half-duplex serial interface between the host
processor and a peripheral, such as a display or a framebuffer chip.
- See http://www.mipi.org/ for DSI spesifications.
+ See http://www.mipi.org/ for DSI specifications.
config OMAP2_DSS_MIN_FCK_PER_PCK
int "Minimum FCK/PCK ratio (for scaling)"
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index ab22cc224f3e..0fefc68372b9 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -104,6 +104,7 @@ struct mgr_priv_data {
bool shadow_extra_info_dirty;
struct omap_video_timings timings;
+ struct dss_lcd_mgr_config lcd_config;
};
static struct {
@@ -137,6 +138,7 @@ static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
void dss_apply_init(void)
{
const int num_ovls = dss_feat_get_num_ovls();
+ struct mgr_priv_data *mp;
int i;
spin_lock_init(&data_lock);
@@ -168,16 +170,35 @@ void dss_apply_init(void)
op->user_info = op->info;
}
+
+ /*
+ * Initialize some of the lcd_config fields for TV manager, this lets
+ * us prevent checking if the manager is LCD or TV at some places
+ */
+ mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT];
+
+ mp->lcd_config.video_port_width = 24;
+ mp->lcd_config.clock_info.lck_div = 1;
+ mp->lcd_config.clock_info.pck_div = 1;
}
+/*
+ * A LCD manager's stallmode decides whether it is in manual or auto update. TV
+ * manager is always auto update, stallmode field for TV manager is false by
+ * default
+ */
static bool ovl_manual_update(struct omap_overlay *ovl)
{
- return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ struct mgr_priv_data *mp = get_mgr_priv(ovl->manager);
+
+ return mp->lcd_config.stallmode;
}
static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
- return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ return mp->lcd_config.stallmode;
}
static int dss_check_settings_low(struct omap_overlay_manager *mgr,
@@ -214,7 +235,7 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr,
ois[ovl->id] = oi;
}
- return dss_mgr_check(mgr, mi, &mp->timings, ois);
+ return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois);
}
/*
@@ -537,7 +558,7 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct omap_overlay_info *oi;
- bool ilace, replication;
+ bool replication;
struct mgr_priv_data *mp;
int r;
@@ -550,11 +571,9 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
mp = get_mgr_priv(ovl->manager);
- replication = dss_use_replication(ovl->manager->device, oi->color_mode);
-
- ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
+ replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode);
- r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings);
+ r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings);
if (r) {
/*
* We can't do much here, as this function can be called from
@@ -635,6 +654,24 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
dispc_mgr_set_timings(mgr->id, &mp->timings);
+ /* lcd_config parameters */
+ if (dss_mgr_is_lcd(mgr->id)) {
+ dispc_mgr_set_io_pad_mode(mp->lcd_config.io_pad_mode);
+
+ dispc_mgr_enable_stallmode(mgr->id, mp->lcd_config.stallmode);
+ dispc_mgr_enable_fifohandcheck(mgr->id,
+ mp->lcd_config.fifohandcheck);
+
+ dispc_mgr_set_clock_div(mgr->id, &mp->lcd_config.clock_info);
+
+ dispc_mgr_set_tft_data_lines(mgr->id,
+ mp->lcd_config.video_port_width);
+
+ dispc_lcd_enable_signal_polarity(mp->lcd_config.lcden_sig_polarity);
+
+ dispc_mgr_set_lcd_type_tft(mgr->id);
+ }
+
mp->extra_info_dirty = false;
if (mp->updating)
mp->shadow_extra_info_dirty = true;
@@ -1294,6 +1331,44 @@ void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
mutex_unlock(&apply_lock);
}
+static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ mp->lcd_config = *config;
+ mp->extra_info_dirty = true;
+}
+
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ unsigned long flags;
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ mutex_lock(&apply_lock);
+
+ if (mp->enabled) {
+ DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n",
+ mgr->name);
+ goto out;
+ }
+
+ spin_lock_irqsave(&data_lock, flags);
+
+ dss_apply_mgr_lcd_config(mgr, config);
+
+ dss_write_regs();
+ dss_set_go_bits();
+
+ spin_unlock_irqrestore(&data_lock, flags);
+
+ wait_pending_extra_info_updates();
+
+out:
+ mutex_unlock(&apply_lock);
+}
+
int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 397d4eee11bb..5b289c5f695b 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -119,6 +119,97 @@ enum omap_color_component {
DISPC_COLOR_COMPONENT_UV = 1 << 1,
};
+enum mgr_reg_fields {
+ DISPC_MGR_FLD_ENABLE,
+ DISPC_MGR_FLD_STNTFT,
+ DISPC_MGR_FLD_GO,
+ DISPC_MGR_FLD_TFTDATALINES,
+ DISPC_MGR_FLD_STALLMODE,
+ DISPC_MGR_FLD_TCKENABLE,
+ DISPC_MGR_FLD_TCKSELECTION,
+ DISPC_MGR_FLD_CPR,
+ DISPC_MGR_FLD_FIFOHANDCHECK,
+ /* used to maintain a count of the above fields */
+ DISPC_MGR_FLD_NUM,
+};
+
+static const struct {
+ const char *name;
+ u32 vsync_irq;
+ u32 framedone_irq;
+ u32 sync_lost_irq;
+ struct reg_field reg_desc[DISPC_MGR_FLD_NUM];
+} mgr_desc[] = {
+ [OMAP_DSS_CHANNEL_LCD] = {
+ .name = "LCD",
+ .vsync_irq = DISPC_IRQ_VSYNC,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_DIGIT] = {
+ .name = "DIGIT",
+ .vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
+ .framedone_irq = 0,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 1, 1 },
+ [DISPC_MGR_FLD_STNTFT] = { },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 6, 6 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { },
+ [DISPC_MGR_FLD_STALLMODE] = { },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 12, 12 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 13, 13 },
+ [DISPC_MGR_FLD_CPR] = { },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_LCD2] = {
+ .name = "LCD2",
+ .vsync_irq = DISPC_IRQ_VSYNC2,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE2,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST2,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL2, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL2, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL2, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL2, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL2, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG2, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG2, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG2, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_LCD3] = {
+ .name = "LCD3",
+ .vsync_irq = DISPC_IRQ_VSYNC3,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE3,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST3,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL3, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG3, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3, 16, 16 },
+ },
+ },
+};
+
static void _omap_dispc_set_irqs(void);
static inline void dispc_write_reg(const u16 idx, u32 val)
@@ -131,6 +222,18 @@ static inline u32 dispc_read_reg(const u16 idx)
return __raw_readl(dispc.base + idx);
}
+static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
+{
+ const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+ return REG_GET(rfld.reg, rfld.high, rfld.low);
+}
+
+static void mgr_fld_write(enum omap_channel channel,
+ enum mgr_reg_fields regfld, int val) {
+ const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+ REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);
+}
+
#define SR(reg) \
dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
#define RR(reg) \
@@ -153,6 +256,10 @@ static void dispc_save_context(void)
SR(CONTROL2);
SR(CONFIG2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ SR(CONTROL3);
+ SR(CONFIG3);
+ }
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
SR(DEFAULT_COLOR(i));
@@ -266,6 +373,8 @@ static void dispc_restore_context(void)
RR(GLOBAL_ALPHA);
if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONFIG2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ RR(CONFIG3);
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
RR(DEFAULT_COLOR(i));
@@ -351,6 +460,8 @@ static void dispc_restore_context(void)
RR(CONTROL);
if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONTROL2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ RR(CONTROL3);
/* clear spurious SYNC_LOST_DIGIT interrupts */
dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
@@ -387,101 +498,41 @@ void dispc_runtime_put(void)
WARN_ON(r < 0 && r != -ENOSYS);
}
-static inline bool dispc_mgr_is_lcd(enum omap_channel channel)
-{
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
- return true;
- else
- return false;
-}
-
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
{
- switch (channel) {
- case OMAP_DSS_CHANNEL_LCD:
- return DISPC_IRQ_VSYNC;
- case OMAP_DSS_CHANNEL_LCD2:
- return DISPC_IRQ_VSYNC2;
- case OMAP_DSS_CHANNEL_DIGIT:
- return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
- default:
- BUG();
- return 0;
- }
+ return mgr_desc[channel].vsync_irq;
}
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
{
- switch (channel) {
- case OMAP_DSS_CHANNEL_LCD:
- return DISPC_IRQ_FRAMEDONE;
- case OMAP_DSS_CHANNEL_LCD2:
- return DISPC_IRQ_FRAMEDONE2;
- case OMAP_DSS_CHANNEL_DIGIT:
- return 0;
- default:
- BUG();
- return 0;
- }
+ return mgr_desc[channel].framedone_irq;
}
bool dispc_mgr_go_busy(enum omap_channel channel)
{
- int bit;
-
- if (dispc_mgr_is_lcd(channel))
- bit = 5; /* GOLCD */
- else
- bit = 6; /* GODIGIT */
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- return REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- return REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
}
void dispc_mgr_go(enum omap_channel channel)
{
- int bit;
bool enable_bit, go_bit;
- if (dispc_mgr_is_lcd(channel))
- bit = 0; /* LCDENABLE */
- else
- bit = 1; /* DIGITALENABLE */
-
/* if the channel is not enabled, we don't need GO */
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- enable_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ enable_bit = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE) == 1;
if (!enable_bit)
return;
- if (dispc_mgr_is_lcd(channel))
- bit = 5; /* GOLCD */
- else
- bit = 6; /* GODIGIT */
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- go_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- go_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ go_bit = mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
if (go_bit) {
DSSERR("GO bit not down for channel %d\n", channel);
return;
}
- DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" :
- (channel == OMAP_DSS_CHANNEL_LCD2 ? "LCD2" : "DIGIT"));
+ DSSDBG("GO %s\n", mgr_desc[channel].name);
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);
- else
- REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
+ mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
}
static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
@@ -832,6 +883,15 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
chan = 0;
chan2 = 1;
break;
+ case OMAP_DSS_CHANNEL_LCD3:
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ chan = 0;
+ chan2 = 2;
+ } else {
+ BUG();
+ return;
+ }
+ break;
default:
BUG();
return;
@@ -867,7 +927,14 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
- if (dss_has_feature(FEAT_MGR_LCD2)) {
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ if (FLD_GET(val, 31, 30) == 0)
+ channel = FLD_GET(val, shift, shift);
+ else if (FLD_GET(val, 31, 30) == 1)
+ channel = OMAP_DSS_CHANNEL_LCD2;
+ else
+ channel = OMAP_DSS_CHANNEL_LCD3;
+ } else if (dss_has_feature(FEAT_MGR_LCD2)) {
if (FLD_GET(val, 31, 30) == 0)
channel = FLD_GET(val, shift, shift);
else
@@ -922,16 +989,10 @@ void dispc_enable_gamma_table(bool enable)
static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
{
- u16 reg;
-
- if (channel == OMAP_DSS_CHANNEL_LCD)
- reg = DISPC_CONFIG;
- else if (channel == OMAP_DSS_CHANNEL_LCD2)
- reg = DISPC_CONFIG2;
- else
+ if (channel == OMAP_DSS_CHANNEL_DIGIT)
return;
- REG_FLD_MOD(reg, enable, 15, 15);
+ mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
}
static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
@@ -939,7 +1000,7 @@ static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
{
u32 coef_r, coef_g, coef_b;
- if (!dispc_mgr_is_lcd(channel))
+ if (!dss_mgr_is_lcd(channel))
return;
coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
@@ -1798,7 +1859,7 @@ static int check_horiz_timing_omap3(enum omap_channel channel,
nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
pclk = dispc_mgr_pclk_rate(channel);
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
lclk = dispc_mgr_lclk_rate(channel);
else
lclk = dispc_fclk_rate();
@@ -2086,8 +2147,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
}
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
- bool ilace, bool replication,
- const struct omap_video_timings *mgr_timings)
+ bool replication, const struct omap_video_timings *mgr_timings)
{
struct omap_overlay *ovl = omap_dss_get_overlay(plane);
bool five_taps = true;
@@ -2103,6 +2163,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
u16 out_width, out_height;
enum omap_channel channel;
int x_predecim = 1, y_predecim = 1;
+ bool ilace = mgr_timings->interlace;
channel = dispc_ovl_get_channel_out(plane);
@@ -2254,14 +2315,9 @@ static void dispc_disable_isr(void *data, u32 mask)
static void _enable_lcd_out(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2) {
- REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0);
- /* flush posted write */
- dispc_read_reg(DISPC_CONTROL2);
- } else {
- REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
- dispc_read_reg(DISPC_CONTROL);
- }
+ mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
+ /* flush posted write */
+ mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
@@ -2274,12 +2330,9 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
/* When we disable LCD output, we need to wait until frame is done.
* Otherwise the DSS is still working, and turning off the clocks
* prevents DSS from going to OFF mode */
- is_on = channel == OMAP_DSS_CHANNEL_LCD2 ?
- REG_GET(DISPC_CONTROL2, 0, 0) :
- REG_GET(DISPC_CONTROL, 0, 0);
+ is_on = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
- irq = channel == OMAP_DSS_CHANNEL_LCD2 ? DISPC_IRQ_FRAMEDONE2 :
- DISPC_IRQ_FRAMEDONE;
+ irq = mgr_desc[channel].framedone_irq;
if (!enable && is_on) {
init_completion(&frame_done_completion);
@@ -2384,21 +2437,12 @@ static void dispc_mgr_enable_digit_out(bool enable)
bool dispc_mgr_is_enabled(enum omap_channel channel)
{
- if (channel == OMAP_DSS_CHANNEL_LCD)
- return !!REG_GET(DISPC_CONTROL, 0, 0);
- else if (channel == OMAP_DSS_CHANNEL_DIGIT)
- return !!REG_GET(DISPC_CONTROL, 1, 1);
- else if (channel == OMAP_DSS_CHANNEL_LCD2)
- return !!REG_GET(DISPC_CONTROL2, 0, 0);
- else {
- BUG();
- return false;
- }
+ return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
dispc_mgr_enable_lcd_out(channel, enable);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_enable_digit_out(enable);
@@ -2432,36 +2476,13 @@ void dispc_pck_free_enable(bool enable)
void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);
- else
- REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
+ mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
}
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
- enum omap_lcd_display_type type)
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
{
- int mode;
-
- switch (type) {
- case OMAP_DSS_LCD_DISPLAY_STN:
- mode = 0;
- break;
-
- case OMAP_DSS_LCD_DISPLAY_TFT:
- mode = 1;
- break;
-
- default:
- BUG();
- return;
- }
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);
- else
- REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
+ mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
}
void dispc_set_loadmode(enum omap_dss_load_mode mode)
@@ -2479,24 +2500,14 @@ static void dispc_mgr_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key)
{
- if (ch == OMAP_DSS_CHANNEL_LCD)
- REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
- else if (ch == OMAP_DSS_CHANNEL_DIGIT)
- REG_FLD_MOD(DISPC_CONFIG, type, 13, 13);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);
+ mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type);
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
}
static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
{
- if (ch == OMAP_DSS_CHANNEL_LCD)
- REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
- else if (ch == OMAP_DSS_CHANNEL_DIGIT)
- REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
+ mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable);
}
static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
@@ -2547,10 +2558,7 @@ void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
return;
}
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);
- else
- REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
+ mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
}
void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
@@ -2584,10 +2592,7 @@ void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, enable, 11, 11);
- else
- REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11);
+ mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
}
static bool _dispc_mgr_size_ok(u16 width, u16 height)
@@ -2627,7 +2632,7 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res);
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
timings_ok = timings_ok && _dispc_lcd_timings_ok(timings->hsw,
timings->hfp, timings->hbp,
timings->vsw, timings->vfp,
@@ -2637,9 +2642,16 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
}
static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
- int hfp, int hbp, int vsw, int vfp, int vbp)
+ int hfp, int hbp, int vsw, int vfp, int vbp,
+ enum omap_dss_signal_level vsync_level,
+ enum omap_dss_signal_level hsync_level,
+ enum omap_dss_signal_edge data_pclk_edge,
+ enum omap_dss_signal_level de_level,
+ enum omap_dss_signal_edge sync_pclk_edge)
+
{
- u32 timing_h, timing_v;
+ u32 timing_h, timing_v, l;
+ bool onoff, rf, ipc;
if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
@@ -2657,6 +2669,44 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
+
+ switch (data_pclk_edge) {
+ case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+ ipc = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+ ipc = true;
+ break;
+ case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+ default:
+ BUG();
+ }
+
+ switch (sync_pclk_edge) {
+ case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+ onoff = false;
+ rf = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+ onoff = true;
+ rf = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+ onoff = true;
+ rf = true;
+ break;
+ default:
+ BUG();
+ };
+
+ l = dispc_read_reg(DISPC_POL_FREQ(channel));
+ l |= FLD_VAL(onoff, 17, 17);
+ l |= FLD_VAL(rf, 16, 16);
+ l |= FLD_VAL(de_level, 15, 15);
+ l |= FLD_VAL(ipc, 14, 14);
+ l |= FLD_VAL(hsync_level, 13, 13);
+ l |= FLD_VAL(vsync_level, 12, 12);
+ dispc_write_reg(DISPC_POL_FREQ(channel), l);
}
/* change name to mode? */
@@ -2674,9 +2724,10 @@ void dispc_mgr_set_timings(enum omap_channel channel,
return;
}
- if (dispc_mgr_is_lcd(channel)) {
+ if (dss_mgr_is_lcd(channel)) {
_dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw,
- t.vfp, t.vbp);
+ t.vfp, t.vbp, t.vsync_level, t.hsync_level,
+ t.data_pclk_edge, t.de_level, t.sync_pclk_edge);
xtot = t.x_res + t.hfp + t.hsw + t.hbp;
ytot = t.y_res + t.vfp + t.vsw + t.vbp;
@@ -2687,14 +2738,13 @@ void dispc_mgr_set_timings(enum omap_channel channel,
DSSDBG("pck %u\n", timings->pixel_clock);
DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp);
+ DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
+ t.vsync_level, t.hsync_level, t.data_pclk_edge,
+ t.de_level, t.sync_pclk_edge);
DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
} else {
- enum dss_hdmi_venc_clk_source_select source;
-
- source = dss_get_hdmi_venc_clk_source();
-
- if (source == DSS_VENC_TV_CLK)
+ if (t.interlace == true)
t.y_res /= 2;
}
@@ -2780,7 +2830,7 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
{
unsigned long r;
- if (dispc_mgr_is_lcd(channel)) {
+ if (dss_mgr_is_lcd(channel)) {
int pcd;
u32 l;
@@ -2821,12 +2871,32 @@ unsigned long dispc_core_clk_rate(void)
return fclk / lcd;
}
-void dispc_dump_clocks(struct seq_file *s)
+static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
{
int lcd, pcd;
+ enum omap_dss_clk_source lcd_clk_src;
+
+ seq_printf(s, "- %s -\n", mgr_desc[channel].name);
+
+ lcd_clk_src = dss_get_lcd_clk_source(channel);
+
+ seq_printf(s, "%s clk source = %s (%s)\n", mgr_desc[channel].name,
+ dss_get_generic_clk_source_name(lcd_clk_src),
+ dss_feat_get_clk_source_name(lcd_clk_src));
+
+ dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd);
+
+ seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
+ dispc_mgr_lclk_rate(channel), lcd);
+ seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
+ dispc_mgr_pclk_rate(channel), pcd);
+}
+
+void dispc_dump_clocks(struct seq_file *s)
+{
+ int lcd;
u32 l;
enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
- enum omap_dss_clk_source lcd_clk_src;
if (dispc_runtime_get())
return;
@@ -2847,36 +2917,13 @@ void dispc_dump_clocks(struct seq_file *s)
seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
(dispc_fclk_rate()/lcd), lcd);
}
- seq_printf(s, "- LCD1 -\n");
-
- lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD);
-
- seq_printf(s, "lcd1_clk source = %s (%s)\n",
- dss_get_generic_clk_source_name(lcd_clk_src),
- dss_feat_get_clk_source_name(lcd_clk_src));
-
- dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd);
-
- seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd);
- seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd);
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- seq_printf(s, "- LCD2 -\n");
-
- lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD2);
- seq_printf(s, "lcd2_clk source = %s (%s)\n",
- dss_get_generic_clk_source_name(lcd_clk_src),
- dss_feat_get_clk_source_name(lcd_clk_src));
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD);
- dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd);
-
- seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd);
- seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
- }
+ if (dss_has_feature(FEAT_MGR_LCD2))
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3);
dispc_runtime_put();
}
@@ -2929,6 +2976,12 @@ void dispc_dump_irqs(struct seq_file *s)
PIS(ACBIAS_COUNT_STAT2);
PIS(SYNC_LOST2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ PIS(FRAMEDONE3);
+ PIS(VSYNC3);
+ PIS(ACBIAS_COUNT_STAT3);
+ PIS(SYNC_LOST3);
+ }
#undef PIS
}
#endif
@@ -2940,6 +2993,7 @@ static void dispc_dump_regs(struct seq_file *s)
[OMAP_DSS_CHANNEL_LCD] = "LCD",
[OMAP_DSS_CHANNEL_DIGIT] = "TV",
[OMAP_DSS_CHANNEL_LCD2] = "LCD2",
+ [OMAP_DSS_CHANNEL_LCD3] = "LCD3",
};
const char *ovl_names[] = {
[OMAP_DSS_GFX] = "GFX",
@@ -2972,6 +3026,10 @@ static void dispc_dump_regs(struct seq_file *s)
DUMPREG(DISPC_CONTROL2);
DUMPREG(DISPC_CONFIG2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ DUMPREG(DISPC_CONTROL3);
+ DUMPREG(DISPC_CONFIG3);
+ }
#undef DUMPREG
@@ -3093,41 +3151,8 @@ static void dispc_dump_regs(struct seq_file *s)
#undef DUMPREG
}
-static void _dispc_mgr_set_pol_freq(enum omap_channel channel, bool onoff,
- bool rf, bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi,
- u8 acb)
-{
- u32 l = 0;
-
- DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n",
- onoff, rf, ieo, ipc, ihs, ivs, acbi, acb);
-
- l |= FLD_VAL(onoff, 17, 17);
- l |= FLD_VAL(rf, 16, 16);
- l |= FLD_VAL(ieo, 15, 15);
- l |= FLD_VAL(ipc, 14, 14);
- l |= FLD_VAL(ihs, 13, 13);
- l |= FLD_VAL(ivs, 12, 12);
- l |= FLD_VAL(acbi, 11, 8);
- l |= FLD_VAL(acb, 7, 0);
-
- dispc_write_reg(DISPC_POL_FREQ(channel), l);
-}
-
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
- enum omap_panel_config config, u8 acbi, u8 acb)
-{
- _dispc_mgr_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0,
- (config & OMAP_DSS_LCD_RF) != 0,
- (config & OMAP_DSS_LCD_IEO) != 0,
- (config & OMAP_DSS_LCD_IPC) != 0,
- (config & OMAP_DSS_LCD_IHS) != 0,
- (config & OMAP_DSS_LCD_IVS) != 0,
- acbi, acb);
-}
-
/* with fck as input clock rate, find dispc dividers that produce req_pck */
-void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+void dispc_find_clk_divs(unsigned long req_pck, unsigned long fck,
struct dispc_clock_info *cinfo)
{
u16 pcd_min, pcd_max;
@@ -3138,9 +3163,6 @@ void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
pcd_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD);
pcd_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD);
- if (!is_tft)
- pcd_min = 3;
-
best_pck = 0;
best_ld = 0;
best_pd = 0;
@@ -3192,15 +3214,13 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
return 0;
}
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo)
{
DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
-
- return 0;
}
int dispc_mgr_get_clock_div(enum omap_channel channel,
@@ -3354,6 +3374,8 @@ static void print_irq_status(u32 status)
PIS(SYNC_LOST_DIGIT);
if (dss_has_feature(FEAT_MGR_LCD2))
PIS(SYNC_LOST2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ PIS(SYNC_LOST3);
#undef PIS
printk("\n");
@@ -3450,12 +3472,6 @@ static void dispc_error_worker(struct work_struct *work)
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
- static const unsigned sync_lost_bits[] = {
- DISPC_IRQ_SYNC_LOST,
- DISPC_IRQ_SYNC_LOST_DIGIT,
- DISPC_IRQ_SYNC_LOST2,
- };
-
spin_lock_irqsave(&dispc.irq_lock, flags);
errors = dispc.error_irqs;
dispc.error_irqs = 0;
@@ -3484,7 +3500,7 @@ static void dispc_error_worker(struct work_struct *work)
unsigned bit;
mgr = omap_dss_get_overlay_manager(i);
- bit = sync_lost_bits[i];
+ bit = mgr_desc[i].sync_lost_irq;
if (bit & errors) {
struct omap_dss_device *dssdev = mgr->device;
@@ -3603,6 +3619,8 @@ static void _omap_dispc_initialize_irq(void)
dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
if (dss_feat_get_num_ovls() > 3)
dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index f278080e1063..92d8a9be86fc 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -36,6 +36,8 @@
#define DISPC_CONTROL2 0x0238
#define DISPC_CONFIG2 0x0620
#define DISPC_DIVISOR 0x0804
+#define DISPC_CONTROL3 0x0848
+#define DISPC_CONFIG3 0x084C
/* DISPC overlay registers */
#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \
@@ -118,6 +120,8 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
return 0x0050;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03AC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0814;
default:
BUG();
return 0;
@@ -133,6 +137,8 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
return 0x0058;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B0;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0818;
default:
BUG();
return 0;
@@ -149,6 +155,8 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0400;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0840;
default:
BUG();
return 0;
@@ -165,6 +173,8 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0404;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0844;
default:
BUG();
return 0;
@@ -181,6 +191,8 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0408;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x083C;
default:
BUG();
return 0;
@@ -197,6 +209,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x040C;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0838;
default:
BUG();
return 0;
@@ -213,6 +227,8 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
return 0x0078;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03CC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0834;
default:
BUG();
return 0;
@@ -229,6 +245,8 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C0;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0828;
default:
BUG();
return 0;
@@ -245,6 +263,8 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C4;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x082C;
default:
BUG();
return 0;
@@ -261,6 +281,8 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C8;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0830;
default:
BUG();
return 0;
@@ -277,6 +299,8 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03BC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0824;
default:
BUG();
return 0;
@@ -293,6 +317,8 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B8;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0820;
default:
BUG();
return 0;
@@ -309,6 +335,8 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B4;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x081C;
default:
BUG();
return 0;
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 249010630370..5bd957e85505 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -116,7 +116,7 @@ static ssize_t display_timings_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
- struct omap_video_timings t;
+ struct omap_video_timings t = dssdev->panel.timings;
int r, found;
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
@@ -316,44 +316,6 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_default_get_timings);
-/* Checks if replication logic should be used. Only use for active matrix,
- * when overlay is in RGB12U or RGB16 mode, and LCD interface is
- * 18bpp or 24bpp */
-bool dss_use_replication(struct omap_dss_device *dssdev,
- enum omap_color_mode mode)
-{
- int bpp;
-
- if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
- return false;
-
- if (dssdev->type == OMAP_DISPLAY_TYPE_DPI &&
- (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0)
- return false;
-
- switch (dssdev->type) {
- case OMAP_DISPLAY_TYPE_DPI:
- bpp = dssdev->phy.dpi.data_lines;
- break;
- case OMAP_DISPLAY_TYPE_HDMI:
- case OMAP_DISPLAY_TYPE_VENC:
- case OMAP_DISPLAY_TYPE_SDI:
- bpp = 24;
- break;
- case OMAP_DISPLAY_TYPE_DBI:
- bpp = dssdev->ctrl.pixel_size;
- break;
- case OMAP_DISPLAY_TYPE_DSI:
- bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
- break;
- default:
- BUG();
- return false;
- }
-
- return bpp > 16;
-}
-
void dss_init_device(struct platform_device *pdev,
struct omap_dss_device *dssdev)
{
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 8c2056c9537b..3266be23fc0d 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -38,6 +38,8 @@
static struct {
struct regulator *vdds_dsi_reg;
struct platform_device *dsidev;
+
+ struct dss_lcd_mgr_config mgr_config;
} dpi;
static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk)
@@ -64,7 +66,7 @@ static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
return false;
}
-static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
@@ -72,8 +74,8 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
struct dispc_clock_info dispc_cinfo;
int r;
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, pck_req,
- &dsi_cinfo, &dispc_cinfo);
+ r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
+ &dispc_cinfo);
if (r)
return r;
@@ -83,11 +85,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r) {
- dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
- return r;
- }
+ dpi.mgr_config.clock_info = dispc_cinfo;
*fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
*lck_div = dispc_cinfo.lck_div;
@@ -96,7 +94,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
return 0;
}
-static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
@@ -104,7 +102,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
struct dispc_clock_info dispc_cinfo;
int r;
- r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
+ r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
if (r)
return r;
@@ -112,9 +110,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
if (r)
return r;
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r)
- return r;
+ dpi.mgr_config.clock_info = dispc_cinfo;
*fck = dss_cinfo.fck;
*lck_div = dispc_cinfo.lck_div;
@@ -129,20 +125,14 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
int lck_div = 0, pck_div = 0;
unsigned long fck = 0;
unsigned long pck;
- bool is_tft;
int r = 0;
- dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
- dssdev->panel.acbi, dssdev->panel.acb);
-
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
if (dpi_use_dsi_pll(dssdev))
- r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000,
- &fck, &lck_div, &pck_div);
+ r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
+ &lck_div, &pck_div);
else
- r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,
- &fck, &lck_div, &pck_div);
+ r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
+ &lck_div, &pck_div);
if (r)
return r;
@@ -161,19 +151,18 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
return 0;
}
-static void dpi_basic_init(struct omap_dss_device *dssdev)
+static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
{
- bool is_tft;
+ dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+ dpi.mgr_config.stallmode = false;
+ dpi.mgr_config.fifohandcheck = false;
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+ dpi.mgr_config.video_port_width = dssdev->phy.dpi.data_lines;
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+ dpi.mgr_config.lcden_sig_polarity = 0;
- dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ?
- OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
- dispc_mgr_set_tft_data_lines(dssdev->manager->id,
- dssdev->phy.dpi.data_lines);
+ dss_mgr_set_lcd_config(dssdev->manager, &dpi.mgr_config);
}
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
@@ -206,8 +195,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_get_dispc;
- dpi_basic_init(dssdev);
-
if (dpi_use_dsi_pll(dssdev)) {
r = dsi_runtime_get(dpi.dsidev);
if (r)
@@ -222,6 +209,8 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_set_mode;
+ dpi_config_lcd_manager(dssdev);
+
mdelay(2);
r = dss_mgr_enable(dssdev->manager);
@@ -292,7 +281,6 @@ EXPORT_SYMBOL(dpi_set_timings);
int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
- bool is_tft;
int r;
int lck_div, pck_div;
unsigned long fck;
@@ -305,11 +293,9 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
if (timings->pixel_clock == 0)
return -EINVAL;
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
if (dpi_use_dsi_pll(dssdev)) {
struct dsi_clock_info dsi_cinfo;
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft,
+ r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
timings->pixel_clock * 1000,
&dsi_cinfo, &dispc_cinfo);
@@ -319,7 +305,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
} else {
struct dss_clock_info dss_cinfo;
- r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
+ r = dss_calc_clock_div(timings->pixel_clock * 1000,
&dss_cinfo, &dispc_cinfo);
if (r)
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 14ce8cc079e3..b07e8864f82f 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -331,6 +331,8 @@ struct dsi_data {
unsigned num_lanes_used;
unsigned scp_clk_refcount;
+
+ struct dss_lcd_mgr_config mgr_config;
};
struct dsi_packet_sent_handler_data {
@@ -1085,9 +1087,9 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (enable)
- clk_enable(dsi->sys_clk);
+ clk_prepare_enable(dsi->sys_clk);
else
- clk_disable(dsi->sys_clk);
+ clk_disable_unprepare(dsi->sys_clk);
if (enable && dsi->pll_locked) {
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
@@ -1316,7 +1318,7 @@ static int dsi_calc_clock_rates(struct platform_device *dsidev,
return 0;
}
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
unsigned long req_pck, struct dsi_clock_info *dsi_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
@@ -1335,8 +1337,8 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
dsi->cache_cinfo.clkin == dss_sys_clk) {
DSSDBG("DSI clock info found from cache\n");
*dsi_cinfo = dsi->cache_cinfo;
- dispc_find_clk_divs(is_tft, req_pck,
- dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo);
+ dispc_find_clk_divs(req_pck, dsi_cinfo->dsi_pll_hsdiv_dispc_clk,
+ dispc_cinfo);
return 0;
}
@@ -1402,7 +1404,7 @@ retry:
match = 1;
- dispc_find_clk_divs(is_tft, req_pck,
+ dispc_find_clk_divs(req_pck,
cur.dsi_pll_hsdiv_dispc_clk,
&cur_dispc);
@@ -3631,17 +3633,14 @@ static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev)
static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol;
- int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol;
- int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol;
bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end;
bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
u32 r;
r = dsi_read_reg(dsidev, DSI_CTRL);
- r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */
- r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */
- r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */
+ r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
+ r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */
+ r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */
r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
@@ -4340,52 +4339,101 @@ EXPORT_SYMBOL(omap_dsi_update);
/* Display funcs */
+static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct dispc_clock_info dispc_cinfo;
+ int r;
+ unsigned long long fck;
+
+ fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
+
+ dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
+ dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
+
+ r = dispc_calc_clock_rates(fck, &dispc_cinfo);
+ if (r) {
+ DSSERR("Failed to calc dispc clocks\n");
+ return r;
+ }
+
+ dsi->mgr_config.clock_info = dispc_cinfo;
+
+ return 0;
+}
+
static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct omap_video_timings timings;
int r;
+ u32 irq = 0;
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
u16 dw, dh;
- u32 irq;
- struct omap_video_timings timings = {
- .hsw = 1,
- .hfp = 1,
- .hbp = 1,
- .vsw = 1,
- .vfp = 0,
- .vbp = 0,
- };
dssdev->driver->get_resolution(dssdev, &dw, &dh);
+
timings.x_res = dw;
timings.y_res = dh;
+ timings.hsw = 1;
+ timings.hfp = 1;
+ timings.hbp = 1;
+ timings.vsw = 1;
+ timings.vfp = 0;
+ timings.vbp = 0;
- irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
- DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+ irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
r = omap_dispc_register_isr(dsi_framedone_irq_callback,
(void *) dssdev, irq);
if (r) {
DSSERR("can't get FRAMEDONE irq\n");
- return r;
+ goto err;
}
- dispc_mgr_enable_stallmode(dssdev->manager->id, true);
- dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
-
- dss_mgr_set_timings(dssdev->manager, &timings);
+ dsi->mgr_config.stallmode = true;
+ dsi->mgr_config.fifohandcheck = true;
} else {
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
- dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
+ timings = dssdev->panel.timings;
- dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
+ dsi->mgr_config.stallmode = false;
+ dsi->mgr_config.fifohandcheck = false;
}
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
- dispc_mgr_set_tft_data_lines(dssdev->manager->id,
- dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt));
+ /*
+ * override interlace, logic level and edge related parameters in
+ * omap_video_timings with default values
+ */
+ timings.interlace = false;
+ timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+
+ dss_mgr_set_timings(dssdev->manager, &timings);
+
+ r = dsi_configure_dispc_clocks(dssdev);
+ if (r)
+ goto err1;
+
+ dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+ dsi->mgr_config.video_port_width =
+ dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ dsi->mgr_config.lcden_sig_polarity = 0;
+
+ dss_mgr_set_lcd_config(dssdev->manager, &dsi->mgr_config);
+
return 0;
+err1:
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE)
+ omap_dispc_unregister_isr(dsi_framedone_irq_callback,
+ (void *) dssdev, irq);
+err:
+ return r;
}
static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
@@ -4393,8 +4441,7 @@ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
u32 irq;
- irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
- DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+ irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
omap_dispc_unregister_isr(dsi_framedone_irq_callback,
(void *) dssdev, irq);
@@ -4426,33 +4473,6 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
return 0;
}
-static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
-{
- struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- struct dispc_clock_info dispc_cinfo;
- int r;
- unsigned long long fck;
-
- fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
-
- dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
- dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
-
- r = dispc_calc_clock_rates(fck, &dispc_cinfo);
- if (r) {
- DSSERR("Failed to calc dispc clocks\n");
- return r;
- }
-
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r) {
- DSSERR("Failed to set dispc clocks\n");
- return r;
- }
-
- return 0;
-}
-
static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4474,10 +4494,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
DSSDBG("PLL OK\n");
- r = dsi_configure_dispc_clocks(dssdev);
- if (r)
- goto err2;
-
r = dsi_cio_init(dssdev);
if (r)
goto err2;
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index d2b57197b292..04b4586113e3 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -388,7 +388,8 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
dsi_wait_pll_hsdiv_dispc_active(dsidev);
break;
case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
- BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2);
+ BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 &&
+ channel != OMAP_DSS_CHANNEL_LCD3);
b = 1;
dsidev = dsi_get_dsidev_from_id(1);
dsi_wait_pll_hsdiv_dispc_active(dsidev);
@@ -398,10 +399,12 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
return;
}
- pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12;
+ pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19);
REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */
- ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+ ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
dss.lcd_clk_source[ix] = clk_src;
}
@@ -418,7 +421,8 @@ enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module)
enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
{
if (dss_has_feature(FEAT_LCD_CLK_SRC)) {
- int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+ int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
return dss.lcd_clk_source[ix];
} else {
/* LCD_CLK source is the same as DISPC_FCLK source for
@@ -502,8 +506,7 @@ unsigned long dss_get_dpll4_rate(void)
return 0;
}
-int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
- struct dss_clock_info *dss_cinfo,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
unsigned long prate;
@@ -551,7 +554,7 @@ retry:
fck = clk_get_rate(dss.dss_clk);
fck_div = 1;
- dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+ dispc_find_clk_divs(req_pck, fck, &cur_dispc);
match = 1;
best_dss.fck = fck;
@@ -581,7 +584,7 @@ retry:
match = 1;
- dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+ dispc_find_clk_divs(req_pck, fck, &cur_dispc);
if (abs(cur_dispc.pck - req_pck) <
abs(best_dispc.pck - req_pck)) {
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index dd1092ceaeef..f67afe76f217 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -152,6 +152,25 @@ struct dsi_clock_info {
u16 lp_clk_div;
};
+struct reg_field {
+ u16 reg;
+ u8 high;
+ u8 low;
+};
+
+struct dss_lcd_mgr_config {
+ enum dss_io_pad_mode io_pad_mode;
+
+ bool stallmode;
+ bool fifohandcheck;
+
+ struct dispc_clock_info clock_info;
+
+ int video_port_width;
+
+ int lcden_sig_polarity;
+};
+
struct seq_file;
struct platform_device;
@@ -188,6 +207,8 @@ int dss_mgr_set_device(struct omap_overlay_manager *mgr,
int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
struct omap_video_timings *timings);
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config);
const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr);
bool dss_ovl_is_enabled(struct omap_overlay *ovl);
@@ -210,8 +231,6 @@ void dss_init_device(struct platform_device *pdev,
struct omap_dss_device *dssdev);
void dss_uninit_device(struct platform_device *pdev,
struct omap_dss_device *dssdev);
-bool dss_use_replication(struct omap_dss_device *dssdev,
- enum omap_color_mode mode);
/* manager */
int dss_init_overlay_managers(struct platform_device *pdev);
@@ -223,8 +242,18 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
+ const struct dss_lcd_mgr_config *config,
struct omap_overlay_info **overlay_infos);
+static inline bool dss_mgr_is_lcd(enum omap_channel id)
+{
+ if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
+ id == OMAP_DSS_CHANNEL_LCD3)
+ return true;
+ else
+ return false;
+}
+
/* overlay */
void dss_init_overlays(struct platform_device *pdev);
void dss_uninit_overlays(struct platform_device *pdev);
@@ -234,6 +263,8 @@ int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info);
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings);
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+ enum omap_color_mode mode);
/* DSS */
int dss_init_platform_driver(void) __init;
@@ -268,8 +299,7 @@ unsigned long dss_get_dpll4_rate(void);
int dss_calc_clock_rates(struct dss_clock_info *cinfo);
int dss_set_clock_div(struct dss_clock_info *cinfo);
int dss_get_clock_div(struct dss_clock_info *cinfo);
-int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
- struct dss_clock_info *dss_cinfo,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo);
/* SDI */
@@ -296,7 +326,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev);
int dsi_pll_set_clock_div(struct platform_device *dsidev,
struct dsi_clock_info *cinfo);
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
unsigned long req_pck, struct dsi_clock_info *cinfo,
struct dispc_clock_info *dispc_cinfo);
int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
@@ -330,7 +360,7 @@ static inline int dsi_pll_set_clock_div(struct platform_device *dsidev,
return -ENODEV;
}
static inline int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
- bool is_tft, unsigned long req_pck,
+ unsigned long req_pck,
struct dsi_clock_info *dsi_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
@@ -387,7 +417,7 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode);
bool dispc_mgr_timings_ok(enum omap_channel channel,
const struct omap_video_timings *timings);
unsigned long dispc_fclk_rate(void);
-void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+void dispc_find_clk_divs(unsigned long req_pck, unsigned long fck,
struct dispc_clock_info *cinfo);
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
struct dispc_clock_info *cinfo);
@@ -398,8 +428,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
bool manual_update);
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
- bool ilace, bool replication,
- const struct omap_video_timings *mgr_timings);
+ bool replication, const struct omap_video_timings *mgr_timings);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
@@ -415,16 +444,13 @@ bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
- enum omap_lcd_display_type type);
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
void dispc_mgr_set_timings(enum omap_channel channel,
struct omap_video_timings *timings);
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
- enum omap_panel_config config, u8 acbi, u8 acb);
unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
unsigned long dispc_core_clk_rate(void);
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index bdf469f080e7..996ffcbfed58 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -24,9 +24,9 @@
#include "ti_hdmi.h"
#endif
-#define MAX_DSS_MANAGERS 3
+#define MAX_DSS_MANAGERS 4
#define MAX_DSS_OVERLAYS 4
-#define MAX_DSS_LCD_MANAGERS 2
+#define MAX_DSS_LCD_MANAGERS 3
#define MAX_NUM_DSI 2
/* DSS has feature id */
@@ -36,6 +36,7 @@ enum dss_feat_id {
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_MGR_LCD2,
+ FEAT_MGR_LCD3,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 26a2430a7028..060216fdc578 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -78,43 +78,214 @@ static struct {
*/
static const struct hdmi_config cea_timings[] = {
-{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} },
-{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} },
-{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} },
-{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} },
-{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} },
-{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} },
-{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} },
-{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} },
-{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} },
+ {
+ { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 1, HDMI_HDMI },
+ },
+ {
+ { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 2, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 4, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 5, HDMI_HDMI },
+ },
+ {
+ { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 6, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 16, HDMI_HDMI },
+ },
+ {
+ { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 17, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 19, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 20, HDMI_HDMI },
+ },
+ {
+ { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 21, HDMI_HDMI },
+ },
+ {
+ { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 29, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 31, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 32, HDMI_HDMI },
+ },
+ {
+ { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 35, HDMI_HDMI },
+ },
+ {
+ { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 37, HDMI_HDMI },
+ },
};
+
static const struct hdmi_config vesa_timings[] = {
/* VESA From Here */
-{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} },
-{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} },
-{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} },
-{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} },
-{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} },
-{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} },
-{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} },
-{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} },
-{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} },
-{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} },
-{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} },
-{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} },
-{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} },
-{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} },
-{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} },
-{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} },
-{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} },
-{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} }
+ {
+ { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 4, HDMI_DVI },
+ },
+ {
+ { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 9, HDMI_DVI },
+ },
+ {
+ { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0xE, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x17, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x1C, HDMI_DVI },
+ },
+ {
+ { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x27, HDMI_DVI },
+ },
+ {
+ { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x20, HDMI_DVI },
+ },
+ {
+ { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x23, HDMI_DVI },
+ },
+ {
+ { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x10, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2A, HDMI_DVI },
+ },
+ {
+ { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2F, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x3A, HDMI_DVI },
+ },
+ {
+ { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x51, HDMI_DVI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x52, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x16, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x29, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x39, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x1B, HDMI_DVI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x55, HDMI_DVI },
+ },
};
static int hdmi_runtime_get(void)
@@ -179,7 +350,7 @@ static const struct hdmi_config *hdmi_get_timings(void)
}
static bool hdmi_timings_compare(struct omap_video_timings *timing1,
- const struct hdmi_video_timings *timing2)
+ const struct omap_video_timings *timing2)
{
int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
@@ -758,6 +929,7 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
hdmi.ip_data.phy_offset = HDMI_PHY;
+ mutex_init(&hdmi.ip_data.lock);
hdmi_panel_init();
@@ -785,7 +957,7 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
static int hdmi_runtime_suspend(struct device *dev)
{
- clk_disable(hdmi.sys_clk);
+ clk_disable_unprepare(hdmi.sys_clk);
dispc_runtime_put();
@@ -800,7 +972,7 @@ static int hdmi_runtime_resume(struct device *dev)
if (r < 0)
return r;
- clk_enable(hdmi.sys_clk);
+ clk_prepare_enable(hdmi.sys_clk);
return 0;
}
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 1179e3c4b1c7..e10844faadf9 100644
--- a/drivers/video/omap2/dss/hdmi_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -43,10 +43,11 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
{
DSSDBG("ENTER hdmi_panel_probe\n");
- dssdev->panel.config = OMAP_DSS_LCD_TFT |
- OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS;
-
- dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31};
+ dssdev->panel.timings = (struct omap_video_timings)
+ { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false,
+ };
DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
dssdev->panel.timings.x_res,
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 0cbcde4c688a..53710fadc82d 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -500,16 +500,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
if (r)
return r;
- if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
+ if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC)
irq = DISPC_IRQ_EVSYNC_ODD;
- } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
+ else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
irq = DISPC_IRQ_EVSYNC_EVEN;
- } else {
- if (mgr->id == OMAP_DSS_CHANNEL_LCD)
- irq = DISPC_IRQ_VSYNC;
- else
- irq = DISPC_IRQ_VSYNC2;
- }
+ else
+ irq = dispc_mgr_get_vsync_irq(mgr->id);
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
@@ -545,6 +541,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->name = "lcd2";
mgr->id = OMAP_DSS_CHANNEL_LCD2;
break;
+ case 3:
+ mgr->name = "lcd3";
+ mgr->id = OMAP_DSS_CHANNEL_LCD3;
+ break;
}
mgr->set_device = &dss_mgr_set_device;
@@ -665,9 +665,40 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
return 0;
}
+static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ struct dispc_clock_info cinfo = config->clock_info;
+ int dl = config->video_port_width;
+ bool stallmode = config->stallmode;
+ bool fifohandcheck = config->fifohandcheck;
+
+ if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
+ return -EINVAL;
+
+ if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
+ return -EINVAL;
+
+ if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
+ return -EINVAL;
+
+ /* fifohandcheck should be used only with stallmode */
+ if (stallmode == false && fifohandcheck == true)
+ return -EINVAL;
+
+ /*
+ * io pad mode can be only checked by using dssdev connected to the
+ * manager. Ignore checking these for now, add checks when manager
+ * is capable of holding information related to the connected interface
+ */
+
+ return 0;
+}
+
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
+ const struct dss_lcd_mgr_config *lcd_config,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl;
@@ -683,6 +714,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
if (r)
return r;
+ r = dss_mgr_check_lcd_config(mgr, lcd_config);
+ if (r)
+ return r;
+
list_for_each_entry(ovl, &mgr->overlays, list) {
struct omap_overlay_info *oi;
int r;
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index b0ba60f88dd2..952c6fad9a81 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -528,14 +528,24 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
struct omap_overlay_manager *lcd_mgr;
struct omap_overlay_manager *tv_mgr;
struct omap_overlay_manager *lcd2_mgr = NULL;
+ struct omap_overlay_manager *lcd3_mgr = NULL;
struct omap_overlay_manager *mgr = NULL;
- lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
- tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
+ lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD);
+ tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ lcd3_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD3);
if (dss_has_feature(FEAT_MGR_LCD2))
- lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2);
-
- if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
+ lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2);
+
+ if (dssdev->channel == OMAP_DSS_CHANNEL_LCD3) {
+ if (!lcd3_mgr->device || force) {
+ if (lcd3_mgr->device)
+ lcd3_mgr->unset_device(lcd3_mgr);
+ lcd3_mgr->set_device(lcd3_mgr, dssdev);
+ mgr = lcd3_mgr;
+ }
+ } else if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
if (!lcd2_mgr->device || force) {
if (lcd2_mgr->device)
lcd2_mgr->unset_device(lcd2_mgr);
@@ -677,3 +687,16 @@ int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
return 0;
}
+
+/*
+ * Checks if replication logic should be used. Only use when overlay is in
+ * RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp
+ */
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+ enum omap_color_mode mode)
+{
+ if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
+ return false;
+
+ return config.video_port_width > 16;
+}
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index 7985fa12b9b4..7c087424b634 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -300,10 +300,11 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
}
EXPORT_SYMBOL(omap_rfbi_write_pixels);
-static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
+static int rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
u16 height, void (*callback)(void *data), void *data)
{
u32 l;
+ int r;
struct omap_video_timings timings = {
.hsw = 1,
.hfp = 1,
@@ -322,7 +323,9 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
dss_mgr_set_timings(dssdev->manager, &timings);
- dispc_mgr_enable(dssdev->manager->id, true);
+ r = dss_mgr_enable(dssdev->manager);
+ if (r)
+ return r;
rfbi.framedone_callback = callback;
rfbi.framedone_callback_data = data;
@@ -335,6 +338,8 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
l = FLD_MOD(l, 1, 4, 4); /* ITE */
rfbi_write_reg(RFBI_CONTROL, l);
+
+ return 0;
}
static void framedone_callback(void *data, u32 mask)
@@ -814,8 +819,11 @@ int omap_rfbi_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(void *), void *data)
{
- rfbi_transfer_area(dssdev, w, h, callback, data);
- return 0;
+ int r;
+
+ r = rfbi_transfer_area(dssdev, w, h, callback, data);
+
+ return r;
}
EXPORT_SYMBOL(omap_rfbi_update);
@@ -859,6 +867,22 @@ static void rfbi_dump_regs(struct seq_file *s)
#undef DUMPREG
}
+static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev)
+{
+ struct dss_lcd_mgr_config mgr_config;
+
+ mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI;
+
+ mgr_config.stallmode = true;
+ /* Do we need fifohandcheck for RFBI? */
+ mgr_config.fifohandcheck = false;
+
+ mgr_config.video_port_width = dssdev->ctrl.pixel_size;
+ mgr_config.lcden_sig_polarity = 0;
+
+ dss_mgr_set_lcd_config(dssdev->manager, &mgr_config);
+}
+
int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
{
int r;
@@ -885,13 +909,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
goto err1;
}
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
-
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI);
- dispc_mgr_enable_stallmode(dssdev->manager->id, true);
-
- dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
+ rfbi_config_lcd_manager(dssdev);
rfbi_configure(dssdev->phy.rfbi.channel,
dssdev->ctrl.pixel_size,
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 3a43dc2a9b46..5d31699fbd3c 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -32,19 +32,21 @@
static struct {
bool update_enabled;
struct regulator *vdds_sdi_reg;
-} sdi;
-static void sdi_basic_init(struct omap_dss_device *dssdev)
+ struct dss_lcd_mgr_config mgr_config;
+} sdi;
+static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
{
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+ sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+ sdi.mgr_config.stallmode = false;
+ sdi.mgr_config.fifohandcheck = false;
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
+ sdi.mgr_config.video_port_width = 24;
+ sdi.mgr_config.lcden_sig_polarity = 1;
- dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24);
- dispc_lcd_enable_signal_polarity(1);
+ dss_mgr_set_lcd_config(dssdev->manager, &sdi.mgr_config);
}
int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
@@ -52,8 +54,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
struct omap_video_timings *t = &dssdev->panel.timings;
struct dss_clock_info dss_cinfo;
struct dispc_clock_info dispc_cinfo;
- u16 lck_div, pck_div;
- unsigned long fck;
unsigned long pck;
int r;
@@ -76,24 +76,17 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_get_dispc;
- sdi_basic_init(dssdev);
-
/* 15.5.9.1.2 */
- dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
-
- dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
- dssdev->panel.acbi, dssdev->panel.acb);
+ dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
- r = dss_calc_clock_div(1, t->pixel_clock * 1000,
- &dss_cinfo, &dispc_cinfo);
+ r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo);
if (r)
goto err_calc_clock_div;
- fck = dss_cinfo.fck;
- lck_div = dispc_cinfo.lck_div;
- pck_div = dispc_cinfo.pck_div;
+ sdi.mgr_config.clock_info = dispc_cinfo;
- pck = fck / lck_div / pck_div / 1000;
+ pck = dss_cinfo.fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000;
if (pck != t->pixel_clock) {
DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
@@ -110,9 +103,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_set_dss_clock_div;
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r)
- goto err_set_dispc_clock_div;
+ sdi_config_lcd_manager(dssdev);
dss_sdi_init(dssdev->phy.sdi.datapairs);
r = dss_sdi_enable();
@@ -129,7 +120,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
err_mgr_enable:
dss_sdi_disable();
err_sdi_enable:
-err_set_dispc_clock_div:
err_set_dss_clock_div:
err_calc_clock_div:
dispc_runtime_put();
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index e734cb444bc7..b046c208cb97 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -42,30 +42,13 @@ enum hdmi_clk_refsel {
HDMI_REFSEL_SYSCLK = 3
};
-/* HDMI timing structure */
-struct hdmi_video_timings {
- u16 x_res;
- u16 y_res;
- /* Unit: KHz */
- u32 pixel_clock;
- u16 hsw;
- u16 hfp;
- u16 hbp;
- u16 vsw;
- u16 vfp;
- u16 vbp;
- bool vsync_pol;
- bool hsync_pol;
- bool interlace;
-};
-
struct hdmi_cm {
int code;
int mode;
};
struct hdmi_config {
- struct hdmi_video_timings timings;
+ struct omap_video_timings timings;
struct hdmi_cm cm;
};
@@ -177,7 +160,7 @@ struct hdmi_ip_data {
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
int hpd_gpio;
- bool phy_tx_enabled;
+ struct mutex lock;
};
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index 4dae1b291079..c23b85a20cdc 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -157,6 +157,10 @@ static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
/* PHY_PWR_CMD */
static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
{
+ /* Return if already the state */
+ if (REG_GET(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, 5, 4) == val)
+ return 0;
+
/* Command for power control of HDMI PHY */
REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
@@ -231,21 +235,13 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
{
- unsigned long flags;
bool hpd;
int r;
- /* this should be in ti_hdmi_4xxx_ip private data */
- static DEFINE_SPINLOCK(phy_tx_lock);
- spin_lock_irqsave(&phy_tx_lock, flags);
+ mutex_lock(&ip_data->lock);
hpd = gpio_get_value(ip_data->hpd_gpio);
- if (hpd == ip_data->phy_tx_enabled) {
- spin_unlock_irqrestore(&phy_tx_lock, flags);
- return 0;
- }
-
if (hpd)
r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
else
@@ -257,9 +253,8 @@ static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
goto err;
}
- ip_data->phy_tx_enabled = hpd;
err:
- spin_unlock_irqrestore(&phy_tx_lock, flags);
+ mutex_unlock(&ip_data->lock);
return r;
}
@@ -327,7 +322,6 @@ void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
- ip_data->phy_tx_enabled = false;
}
static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
@@ -747,11 +741,15 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
{
u32 r;
+ bool vsync_pol, hsync_pol;
pr_debug("Enter hdmi_wp_video_config_interface\n");
+ vsync_pol = ip_data->cfg.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+ hsync_pol = ip_data->cfg.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+
r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7);
- r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6);
+ r = FLD_MOD(r, vsync_pol, 7, 7);
+ r = FLD_MOD(r, hsync_pol, 6, 6);
r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 3907c8b6ecbc..3a220877461a 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -272,6 +272,8 @@ const struct omap_video_timings omap_dss_pal_timings = {
.vsw = 5,
.vfp = 5,
.vbp = 41,
+
+ .interlace = true,
};
EXPORT_SYMBOL(omap_dss_pal_timings);
@@ -285,6 +287,8 @@ const struct omap_video_timings omap_dss_ntsc_timings = {
.vsw = 6,
.vfp = 6,
.vbp = 31,
+
+ .interlace = true,
};
EXPORT_SYMBOL(omap_dss_ntsc_timings);
@@ -930,7 +934,7 @@ static int __exit omap_venchw_remove(struct platform_device *pdev)
static int venc_runtime_suspend(struct device *dev)
{
if (venc.tv_dac_clk)
- clk_disable(venc.tv_dac_clk);
+ clk_disable_unprepare(venc.tv_dac_clk);
dispc_runtime_put();
@@ -946,7 +950,7 @@ static int venc_runtime_resume(struct device *dev)
return r;
if (venc.tv_dac_clk)
- clk_enable(venc.tv_dac_clk);
+ clk_prepare_enable(venc.tv_dac_clk);
return 0;
}
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 3450ea0966c9..08ec1a7103f2 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -733,6 +733,12 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->lower_margin = timings.vfp;
var->hsync_len = timings.hsw;
var->vsync_len = timings.vsw;
+ var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+ FB_SYNC_HOR_HIGH_ACT : 0;
+ var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+ FB_SYNC_VERT_HIGH_ACT : 0;
+ var->vmode = timings.interlace ?
+ FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
} else {
var->pixclock = 0;
var->left_margin = 0;
@@ -741,12 +747,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->lower_margin = 0;
var->hsync_len = 0;
var->vsync_len = 0;
+ var->sync = 0;
+ var->vmode = FB_VMODE_NONINTERLACED;
}
- /* TODO: get these from panel->config */
- var->vmode = FB_VMODE_NONINTERLACED;
- var->sync = 0;
-
return 0;
}
@@ -1993,6 +1997,7 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
}
static int omapfb_mode_to_timings(const char *mode_str,
+ struct omap_dss_device *display,
struct omap_video_timings *timings, u8 *bpp)
{
struct fb_info *fbi;
@@ -2046,6 +2051,14 @@ static int omapfb_mode_to_timings(const char *mode_str,
goto err;
}
+ if (display->driver->get_timings) {
+ display->driver->get_timings(display, timings);
+ } else {
+ timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+ }
+
timings->pixel_clock = PICOS2KHZ(var->pixclock);
timings->hbp = var->left_margin;
timings->hfp = var->right_margin;
@@ -2055,6 +2068,13 @@ static int omapfb_mode_to_timings(const char *mode_str,
timings->vsw = var->vsync_len;
timings->x_res = var->xres;
timings->y_res = var->yres;
+ timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ timings->interlace = var->vmode & FB_VMODE_INTERLACED;
switch (var->bits_per_pixel) {
case 16:
@@ -2085,7 +2105,7 @@ static int omapfb_set_def_mode(struct omapfb2_device *fbdev,
struct omap_video_timings timings, temp_timings;
struct omapfb_display_data *d;
- r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
+ r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp);
if (r)
return r;
@@ -2178,8 +2198,17 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
}
static void fb_videomode_to_omap_timings(struct fb_videomode *m,
+ struct omap_dss_device *display,
struct omap_video_timings *t)
{
+ if (display->driver->get_timings) {
+ display->driver->get_timings(display, t);
+ } else {
+ t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ t->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+ }
+
t->x_res = m->xres;
t->y_res = m->yres;
t->pixel_clock = PICOS2KHZ(m->pixclock);
@@ -2189,6 +2218,13 @@ static void fb_videomode_to_omap_timings(struct fb_videomode *m,
t->vsw = m->vsync_len;
t->vfp = m->lower_margin;
t->vbp = m->upper_margin;
+ t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ t->interlace = m->vmode & FB_VMODE_INTERLACED;
}
static int omapfb_find_best_mode(struct omap_dss_device *display,
@@ -2231,7 +2267,7 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
if (m->xres == 2880 || m->xres == 1440)
continue;
- fb_videomode_to_omap_timings(m, &t);
+ fb_videomode_to_omap_timings(m, display, &t);
r = display->driver->check_timings(display, &t);
if (r == 0 && best_xres < m->xres) {
@@ -2245,7 +2281,8 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
goto err2;
}
- fb_videomode_to_omap_timings(&specs->modedb[best_idx], timings);
+ fb_videomode_to_omap_timings(&specs->modedb[best_idx], display,
+ timings);
r = 0;
diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c
index 2c80246b18b8..1d007366b917 100644
--- a/drivers/video/s3fb.c
+++ b/drivers/video/s3fb.c
@@ -84,7 +84,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
"S3 Virge/VX", "S3 Virge/DX", "S3 Virge/GX",
"S3 Virge/GX2", "S3 Virge/GX2+", "",
"S3 Trio3D/1X", "S3 Trio3D/2X", "S3 Trio3D/2X",
- "S3 Trio3D"};
+ "S3 Trio3D", "S3 Virge/MX"};
#define CHIP_UNKNOWN 0x00
#define CHIP_732_TRIO32 0x01
@@ -105,6 +105,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
#define CHIP_362_TRIO3D_2X 0x11
#define CHIP_368_TRIO3D_2X 0x12
#define CHIP_365_TRIO3D 0x13
+#define CHIP_260_VIRGE_MX 0x14
#define CHIP_XXX_TRIO 0x80
#define CHIP_XXX_TRIO64V2_DXGX 0x81
@@ -280,7 +281,8 @@ static int __devinit s3fb_setup_ddc_bus(struct fb_info *info)
*/
/* vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */
if (par->chip == CHIP_357_VIRGE_GX2 ||
- par->chip == CHIP_359_VIRGE_GX2P)
+ par->chip == CHIP_359_VIRGE_GX2P ||
+ par->chip == CHIP_260_VIRGE_MX)
svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03);
else
svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03);
@@ -487,7 +489,8 @@ static void s3_set_pixclock(struct fb_info *info, u32 pixclock)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X) {
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX) {
vga_wseq(par->state.vgabase, 0x12, (n - 2) | ((r & 3) << 6)); /* n and two bits of r */
vga_wseq(par->state.vgabase, 0x29, r >> 2); /* remaining highest bit of r */
} else
@@ -690,7 +693,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X) {
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX) {
vga_wcrt(par->state.vgabase, 0x54, 0x18); /* M parameter */
vga_wcrt(par->state.vgabase, 0x60, 0xff); /* N parameter */
vga_wcrt(par->state.vgabase, 0x61, 0xff); /* L parameter */
@@ -739,7 +743,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_368_TRIO3D_2X ||
par->chip == CHIP_365_TRIO3D ||
par->chip == CHIP_375_VIRGE_DX ||
- par->chip == CHIP_385_VIRGE_GX) {
+ par->chip == CHIP_385_VIRGE_GX ||
+ par->chip == CHIP_260_VIRGE_MX) {
dbytes = info->var.xres * ((bpp+7)/8);
vga_wcrt(par->state.vgabase, 0x91, (dbytes + 7) / 8);
vga_wcrt(par->state.vgabase, 0x90, (((dbytes + 7) / 8) >> 8) | 0x80);
@@ -751,7 +756,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X)
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX)
vga_wcrt(par->state.vgabase, 0x34, 0x00);
else /* enable Data Transfer Position Control (DTPC) */
vga_wcrt(par->state.vgabase, 0x34, 0x10);
@@ -807,7 +813,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X)
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX)
svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0);
else {
svga_wcrt_mask(par->state.vgabase, 0x67, 0x10, 0xF0);
@@ -837,7 +844,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X)
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX)
hmul = 2;
}
break;
@@ -864,7 +872,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X)
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX)
hmul = 2;
}
break;
@@ -1208,7 +1217,8 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i
break;
}
} else if (par->chip == CHIP_357_VIRGE_GX2 ||
- par->chip == CHIP_359_VIRGE_GX2P) {
+ par->chip == CHIP_359_VIRGE_GX2P ||
+ par->chip == CHIP_260_VIRGE_MX) {
switch ((regval & 0xC0) >> 6) {
case 1: /* 4MB */
info->screen_size = 4 << 20;
@@ -1515,6 +1525,7 @@ static struct pci_device_id s3_devices[] __devinitdata = {
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P},
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X},
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8904), .driver_data = CHIP_365_TRIO3D},
+ {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX},
{0, 0, 0, 0, 0, 0, 0}
};
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 4c6b84488561..3951fdae5f68 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -127,8 +127,7 @@ static void sh_mipi_shutdown(struct platform_device *pdev)
sh_mipi_dsi_enable(mipi, false);
}
-static int __init sh_mipi_setup(struct sh_mipi *mipi,
- struct sh_mipi_dsi_info *pdata)
+static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
{
void __iomem *base = mipi->base;
struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
@@ -551,7 +550,7 @@ efindslot:
return ret;
}
-static int __exit sh_mipi_remove(struct platform_device *pdev)
+static int __devexit sh_mipi_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -592,7 +591,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
}
static struct platform_driver sh_mipi_driver = {
- .remove = __exit_p(sh_mipi_remove),
+ .remove = __devexit_p(sh_mipi_remove),
.shutdown = sh_mipi_shutdown,
.driver = {
.name = "sh-mipi-dsi",
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index e672698bd820..699487c287b2 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -12,6 +12,7 @@
#include <linux/backlight.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -32,12 +33,176 @@
#include "sh_mobile_lcdcfb.h"
+/* ----------------------------------------------------------------------------
+ * Overlay register definitions
+ */
+
+#define LDBCR 0xb00
+#define LDBCR_UPC(n) (1 << ((n) + 16))
+#define LDBCR_UPF(n) (1 << ((n) + 8))
+#define LDBCR_UPD(n) (1 << ((n) + 0))
+#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN (1 << 31)
+#define LDBBSIFR_VS (1 << 29)
+#define LDBBSIFR_BRSEL (1 << 28)
+#define LDBBSIFR_MX (1 << 27)
+#define LDBBSIFR_MY (1 << 26)
+#define LDBBSIFR_CV3 (3 << 24)
+#define LDBBSIFR_CV2 (2 << 24)
+#define LDBBSIFR_CV1 (1 << 24)
+#define LDBBSIFR_CV0 (0 << 24)
+#define LDBBSIFR_CV_MASK (3 << 24)
+#define LDBBSIFR_LAY_MASK (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT 16
+#define LDBBSIFR_ROP3_MASK (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT 16
+#define LDBBSIFR_AL_PL8 (3 << 14)
+#define LDBBSIFR_AL_PL1 (2 << 14)
+#define LDBBSIFR_AL_PK (1 << 14)
+#define LDBBSIFR_AL_1 (0 << 14)
+#define LDBBSIFR_AL_MASK (3 << 14)
+#define LDBBSIFR_SWPL (1 << 10)
+#define LDBBSIFR_SWPW (1 << 9)
+#define LDBBSIFR_SWPB (1 << 8)
+#define LDBBSIFR_RY (1 << 7)
+#define LDBBSIFR_CHRR_420 (2 << 0)
+#define LDBBSIFR_CHRR_422 (1 << 0)
+#define LDBBSIFR_CHRR_444 (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16 (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24 (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK (0x1f << 0)
+#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT 16
+#define LDBBSSZR_BHSS_MASK (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT 0
+#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT 16
+#define LDBBLOCR_CHLC_MASK (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT 0
+#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT 16
+#define LDBBSMWR_BSMW_MASK (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT 0
+#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT 24
+#define LDBBSAYR_FG1R_MASK (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT 16
+#define LDBBSAYR_FG1G_MASK (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT 8
+#define LDBBSAYR_FG1B_MASK (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT 0
+#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT 24
+#define LDBBSACR_FG2R_MASK (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT 16
+#define LDBBSACR_FG2G_MASK (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT 8
+#define LDBBSACR_FG2B_MASK (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT 0
+#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK (0xff << 24)
+#define LDBBSAAR_AP_SHIFT 24
+#define LDBBSAAR_R_MASK (0xff << 16)
+#define LDBBSAAR_R_SHIFT 16
+#define LDBBSAAR_GY_MASK (0xff << 8)
+#define LDBBSAAR_GY_SHIFT 8
+#define LDBBSAAR_B_MASK (0xff << 0)
+#define LDBBSAAR_B_SHIFT 0
+#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK (0xff << 24)
+#define LDBBPPCR_AP_SHIFT 24
+#define LDBBPPCR_R_MASK (0xff << 16)
+#define LDBBPPCR_R_SHIFT 16
+#define LDBBPPCR_GY_MASK (0xff << 8)
+#define LDBBPPCR_GY_SHIFT 8
+#define LDBBPPCR_B_MASK (0xff << 0)
+#define LDBBPPCR_B_SHIFT 0
+#define LDBnBBGCL(n) (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT 24
+#define LDBBBGCL_BGR_MASK (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT 16
+#define LDBBBGCL_BGG_MASK (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT 8
+#define LDBBBGCL_BGB_MASK (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT 0
+
#define SIDE_B_OFFSET 0x1000
#define MIRROR_OFFSET 0x2000
#define MAX_XRES 1920
#define MAX_YRES 1080
+enum sh_mobile_lcdc_overlay_mode {
+ LCDC_OVERLAY_BLEND,
+ LCDC_OVERLAY_ROP3,
+};
+
+/*
+ * struct sh_mobile_lcdc_overlay - LCDC display overlay
+ *
+ * @channel: LCDC channel this overlay belongs to
+ * @cfg: Overlay configuration
+ * @info: Frame buffer device
+ * @index: Overlay index (0-3)
+ * @base: Overlay registers base address
+ * @enabled: True if the overlay is enabled
+ * @mode: Overlay blending mode (alpha blend or ROP3)
+ * @alpha: Global alpha blending value (0-255, for alpha blending mode)
+ * @rop3: Raster operation (for ROP3 mode)
+ * @fb_mem: Frame buffer virtual memory address
+ * @fb_size: Frame buffer size in bytes
+ * @dma_handle: Frame buffer DMA address
+ * @base_addr_y: Overlay base address (RGB or luma component)
+ * @base_addr_c: Overlay base address (chroma component)
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
+ * @format: Current pixelf format
+ * @xres: Horizontal visible resolution
+ * @xres_virtual: Horizontal total resolution
+ * @yres: Vertical visible resolution
+ * @yres_virtual: Vertical total resolution
+ * @pitch: Overlay line pitch
+ * @pos_x: Horizontal overlay position
+ * @pos_y: Vertical overlay position
+ */
+struct sh_mobile_lcdc_overlay {
+ struct sh_mobile_lcdc_chan *channel;
+
+ const struct sh_mobile_lcdc_overlay_cfg *cfg;
+ struct fb_info *info;
+
+ unsigned int index;
+ unsigned long base;
+
+ bool enabled;
+ enum sh_mobile_lcdc_overlay_mode mode;
+ unsigned int alpha;
+ unsigned int rop3;
+
+ void *fb_mem;
+ unsigned long fb_size;
+
+ dma_addr_t dma_handle;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long pan_y_offset;
+
+ const struct sh_mobile_lcdc_format_info *format;
+ unsigned int xres;
+ unsigned int xres_virtual;
+ unsigned int yres;
+ unsigned int yres_virtual;
+ unsigned int pitch;
+ int pos_x;
+ int pos_y;
+};
+
struct sh_mobile_lcdc_priv {
void __iomem *base;
int irq;
@@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv {
struct device *dev;
struct clk *dot_clk;
unsigned long lddckr;
+
struct sh_mobile_lcdc_chan ch[2];
+ struct sh_mobile_lcdc_overlay overlays[4];
+
struct notifier_block notifier;
int started;
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
@@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
}
+static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl,
+ int reg, unsigned long data)
+{
+ iowrite32(data, ovl->channel->lcdc->base + reg);
+ iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET);
+}
+
static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
unsigned long reg_offs, unsigned long data)
{
@@ -384,8 +559,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
return true;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info);
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
enum sh_mobile_lcdc_entity_event event,
@@ -439,7 +614,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
fb_videomode_to_var(&var, mode);
var.bits_per_pixel = info->var.bits_per_pixel;
var.grayscale = info->var.grayscale;
- ret = sh_mobile_check_var(&var, info);
+ ret = sh_mobile_lcdc_check_var(&var, info);
break;
}
@@ -585,7 +760,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
{
unsigned long ldintr;
int ret;
@@ -685,8 +860,98 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
lcdc_write_chan(ch, LDHAJR, tmp);
}
+static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ u32 format = 0;
+
+ if (!ovl->enabled) {
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0);
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+ return;
+ }
+
+ ovl->base_addr_y = ovl->dma_handle;
+ ovl->base_addr_c = ovl->dma_handle
+ + ovl->xres_virtual * ovl->yres_virtual;
+
+ switch (ovl->mode) {
+ case LCDC_OVERLAY_BLEND:
+ format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT);
+ break;
+
+ case LCDC_OVERLAY_ROP3:
+ format = LDBBSIFR_EN | LDBBSIFR_BRSEL
+ | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT);
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ default:
+ format |= LDBBSIFR_SWPL;
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+ break;
+ }
+
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format);
+
+ lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index),
+ (ovl->yres << LDBBSSZR_BVSS_SHIFT) |
+ (ovl->xres << LDBBSSZR_BHSS_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index),
+ (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) |
+ (ovl->pos_x << LDBBLOCR_CHLC_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index),
+ ovl->pitch << LDBBSMWR_BSMW_SHIFT);
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+}
+
/*
- * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * __sh_mobile_lcdc_start - Configure and start the LCDC
* @priv: LCDC device
*
* Configure all enabled channels and start the LCDC device. All external
@@ -839,27 +1104,25 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Compute frame buffer base address and pitch for each channel. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
int pixelformat;
- void *meram;
+ void *cache;
ch = &priv->ch[k];
if (!ch->enabled)
continue;
ch->base_addr_y = ch->dma_handle;
- ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
+ ch->base_addr_c = ch->dma_handle
+ + ch->xres_virtual * ch->yres_virtual;
ch->line_size = ch->pitch;
/* Enable MERAM if possible. */
- if (mdev == NULL || mdev->ops == NULL ||
- ch->cfg->meram_cfg == NULL)
+ if (mdev == NULL || ch->cfg->meram_cfg == NULL)
continue;
- /* we need to de-init configured ICBs before we can
- * re-initialize them.
- */
- if (ch->meram) {
- mdev->ops->meram_unregister(mdev, ch->meram);
- ch->meram = NULL;
+ /* Free the allocated MERAM cache. */
+ if (ch->cache) {
+ sh_mobile_meram_cache_free(mdev, ch->cache);
+ ch->cache = NULL;
}
switch (ch->format->fourcc) {
@@ -881,17 +1144,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
break;
}
- meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+ cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg,
ch->pitch, ch->yres, pixelformat,
&ch->line_size);
- if (!IS_ERR(meram)) {
- mdev->ops->meram_update(mdev, meram,
+ if (!IS_ERR(cache)) {
+ sh_mobile_meram_cache_update(mdev, cache,
ch->base_addr_y, ch->base_addr_c,
&ch->base_addr_y, &ch->base_addr_c);
- ch->meram = meram;
+ ch->cache = cache;
}
}
+ for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k];
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
/* Start the LCDC. */
__sh_mobile_lcdc_start(priv);
@@ -953,12 +1221,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_display_off(ch);
- /* disable the meram */
- if (ch->meram) {
- struct sh_mobile_meram_info *mdev;
- mdev = priv->meram_dev;
- mdev->ops->meram_unregister(mdev, ch->meram);
- ch->meram = 0;
+ /* Free the MERAM cache. */
+ if (ch->cache) {
+ sh_mobile_meram_cache_free(priv->meram_dev, ch->cache);
+ ch->cache = 0;
}
}
@@ -975,8 +1241,511 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_clk_off(priv);
}
+static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+ return -EINVAL;
+
+ /* Make sure the virtual resolution is at least as big as the visible
+ * resolution.
+ */
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (sh_mobile_format_is_fourcc(var)) {
+ const struct sh_mobile_lcdc_format_info *format;
+
+ format = sh_mobile_format_info(var->grayscale);
+ if (format == NULL)
+ return -EINVAL;
+ var->bits_per_pixel = format->bpp;
+
+ /* Default to RGB and JPEG color-spaces for RGB and YUV formats
+ * respectively.
+ */
+ if (!format->yuv)
+ var->colorspace = V4L2_COLORSPACE_SRGB;
+ else if (var->colorspace != V4L2_COLORSPACE_REC709)
+ var->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ if (var->bits_per_pixel <= 16) { /* RGB 565 */
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ } else
+ return -EINVAL;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ }
+
+ /* Make sure we don't exceed our allocated memory. */
+ if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
+ info->fix.smem_len)
+ return -EINVAL;
+
+ return 0;
+}
+
/* -----------------------------------------------------------------------------
- * Frame buffer operations
+ * Frame buffer operations - Overlays
+ */
+
+static ssize_t
+overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha);
+}
+
+static ssize_t
+overlay_alpha_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int alpha;
+ char *endp;
+
+ alpha = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (alpha > 255)
+ return -EINVAL;
+
+ if (ovl->alpha != alpha) {
+ ovl->alpha = alpha;
+
+ if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode);
+}
+
+static ssize_t
+overlay_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int mode;
+ char *endp;
+
+ mode = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3)
+ return -EINVAL;
+
+ if (ovl->mode != mode) {
+ ovl->mode = mode;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_position_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y);
+}
+
+static ssize_t
+overlay_position_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ char *endp;
+ int pos_x;
+ int pos_y;
+
+ pos_x = simple_strtol(buf, &endp, 10);
+ if (*endp != ',')
+ return -EINVAL;
+
+ pos_y = simple_strtol(endp + 1, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) {
+ ovl->pos_x = pos_x;
+ ovl->pos_y = pos_y;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3);
+}
+
+static ssize_t
+overlay_rop3_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int rop3;
+ char *endp;
+
+ rop3 = !!simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (rop3 > 255)
+ return -EINVAL;
+
+ if (ovl->rop3 != rop3) {
+ ovl->rop3 = rop3;
+
+ if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static const struct device_attribute overlay_sysfs_attrs[] = {
+ __ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
+ overlay_alpha_show, overlay_alpha_store),
+ __ATTR(ovl_mode, S_IRUGO|S_IWUSR,
+ overlay_mode_show, overlay_mode_store),
+ __ATTR(ovl_position, S_IRUGO|S_IWUSR,
+ overlay_position_show, overlay_position_store),
+ __ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
+ overlay_rop3_show, overlay_rop3_store),
+};
+
+static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = {
+ .id = "SH Mobile LCDC",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+ .xpanstep = 1,
+ .ypanstep = 1,
+ .ywrapstep = 0,
+ .capabilities = FB_CAP_FOURCC,
+};
+
+static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long y_offset;
+ unsigned long c_offset;
+
+ if (!ovl->format->yuv) {
+ y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset)
+ * ovl->format->bpp / 8;
+ c_offset = 0;
+ } else {
+ unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1;
+ unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1;
+
+ y_offset = var->yoffset * ovl->xres_virtual + var->xoffset;
+ c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub
+ + var->xoffset * 2 / xsub;
+ }
+
+ /* If the Y offset hasn't changed, the C offset hasn't either. There's
+ * nothing to do in that case.
+ */
+ if (y_offset == ovl->pan_y_offset)
+ return 0;
+
+ /* Set the source address for the next refresh */
+ base_addr_y = ovl->dma_handle + y_offset;
+ base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual
+ + c_offset;
+
+ ovl->base_addr_y = base_addr_y;
+ ovl->base_addr_c = base_addr_c;
+ ovl->pan_y_offset = y_offset;
+
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+
+ return 0;
+}
+
+static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ return sh_mobile_lcdc_wait_for_vsync(ovl->channel);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ return __sh_mobile_lcdc_check_var(var, info);
+}
+
+static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ ovl->format =
+ sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+
+ ovl->xres = info->var.xres;
+ ovl->xres_virtual = info->var.xres_virtual;
+ ovl->yres = info->var.yres;
+ ovl->yres_virtual = info->var.yres_virtual;
+
+ if (ovl->format->yuv)
+ ovl->pitch = info->var.xres_virtual;
+ else
+ ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8;
+
+ sh_mobile_lcdc_overlay_setup(ovl);
+
+ info->fix.line_length = ovl->pitch;
+
+ if (sh_mobile_format_is_fourcc(&info->var)) {
+ info->fix.type = FB_TYPE_FOURCC;
+ info->fix.visual = FB_VISUAL_FOURCC;
+ } else {
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ }
+
+ return 0;
+}
+
+/* Overlay blanking. Disable the overlay when blanked. */
+static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ ovl->enabled = !blank;
+ sh_mobile_lcdc_overlay_setup(ovl);
+
+ /* Prevent the backlight from receiving a blanking event by returning
+ * a non-zero value.
+ */
+ return 1;
+}
+
+static struct fb_ops sh_mobile_lcdc_overlay_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_blank = sh_mobile_lcdc_overlay_blank,
+ .fb_pan_display = sh_mobile_lcdc_overlay_pan,
+ .fb_ioctl = sh_mobile_lcdc_overlay_ioctl,
+ .fb_check_var = sh_mobile_lcdc_overlay_check_var,
+ .fb_set_par = sh_mobile_lcdc_overlay_set_par,
+};
+
+static void
+sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info == NULL || info->dev == NULL)
+ return;
+
+ unregister_framebuffer(ovl->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
+ struct fb_info *info = ovl->info;
+ unsigned int i;
+ int ret;
+
+ if (info == NULL)
+ return 0;
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return ret;
+
+ dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n",
+ dev_name(lcdc->dev), ovl->index, info->var.xres,
+ info->var.yres, info->var.bits_per_pixel);
+
+ for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
+ ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info == NULL || info->device == NULL)
+ return;
+
+ framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc;
+ struct fb_var_screeninfo *var;
+ struct fb_info *info;
+
+ /* Allocate and initialize the frame buffer device. */
+ info = framebuffer_alloc(0, priv->dev);
+ if (info == NULL) {
+ dev_err(priv->dev, "unable to allocate fb_info\n");
+ return -ENOMEM;
+ }
+
+ ovl->info = info;
+
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &sh_mobile_lcdc_overlay_ops;
+ info->device = priv->dev;
+ info->screen_base = ovl->fb_mem;
+ info->par = ovl;
+
+ /* Initialize fixed screen information. Restrict pan to 2 lines steps
+ * for NV12 and NV21.
+ */
+ info->fix = sh_mobile_lcdc_overlay_fix;
+ snprintf(info->fix.id, sizeof(info->fix.id),
+ "SH Mobile LCDC Overlay %u", ovl->index);
+ info->fix.smem_start = ovl->dma_handle;
+ info->fix.smem_len = ovl->fb_size;
+ info->fix.line_length = ovl->pitch;
+
+ if (ovl->format->yuv)
+ info->fix.visual = FB_VISUAL_FOURCC;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ info->fix.ypanstep = 2;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ info->fix.xpanstep = 2;
+ }
+
+ /* Initialize variable screen information. */
+ var = &info->var;
+ memset(var, 0, sizeof(*var));
+ var->xres = ovl->xres;
+ var->yres = ovl->yres;
+ var->xres_virtual = ovl->xres_virtual;
+ var->yres_virtual = ovl->yres_virtual;
+ var->activate = FB_ACTIVATE_NOW;
+
+ /* Use the legacy API by default for RGB formats, and the FOURCC API
+ * for YUV formats.
+ */
+ if (!ovl->format->yuv)
+ var->bits_per_pixel = ovl->format->bpp;
+ else
+ var->grayscale = ovl->format->fourcc;
+
+ return sh_mobile_lcdc_overlay_check_var(var, info);
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations - main frame buffer
*/
static int sh_mobile_lcdc_setcolreg(u_int regno,
@@ -1003,12 +1772,12 @@ static int sh_mobile_lcdc_setcolreg(u_int regno,
return 0;
}
-static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
+static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.id = "SH Mobile LCDC",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE,
- .xpanstep = 0,
+ .xpanstep = 1,
.ypanstep = 1,
.ywrapstep = 0,
.capabilities = FB_CAP_FOURCC,
@@ -1035,78 +1804,74 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
sh_mobile_lcdc_deferred_io_touch(info);
}
-static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info)
+static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *priv = ch->lcdc;
unsigned long ldrcntr;
- unsigned long new_pan_offset;
unsigned long base_addr_y, base_addr_c;
+ unsigned long y_offset;
unsigned long c_offset;
- if (!ch->format->yuv)
- new_pan_offset = var->yoffset * ch->pitch
- + var->xoffset * (ch->format->bpp / 8);
- else
- new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
+ if (!ch->format->yuv) {
+ y_offset = (var->yoffset * ch->xres_virtual + var->xoffset)
+ * ch->format->bpp / 8;
+ c_offset = 0;
+ } else {
+ unsigned int xsub = ch->format->bpp < 24 ? 2 : 1;
+ unsigned int ysub = ch->format->bpp < 16 ? 2 : 1;
- if (new_pan_offset == ch->pan_offset)
- return 0; /* No change, do nothing */
+ y_offset = var->yoffset * ch->xres_virtual + var->xoffset;
+ c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub
+ + var->xoffset * 2 / xsub;
+ }
- ldrcntr = lcdc_read(priv, _LDRCNTR);
+ /* If the Y offset hasn't changed, the C offset hasn't either. There's
+ * nothing to do in that case.
+ */
+ if (y_offset == ch->pan_y_offset)
+ return 0;
/* Set the source address for the next refresh */
- base_addr_y = ch->dma_handle + new_pan_offset;
- if (ch->format->yuv) {
- /* Set y offset */
- c_offset = var->yoffset * ch->pitch
- * (ch->format->bpp - 8) / 8;
- base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
- + c_offset;
- /* Set x offset */
- if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
- base_addr_c += 2 * var->xoffset;
- else
- base_addr_c += var->xoffset;
- }
+ base_addr_y = ch->dma_handle + y_offset;
+ base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual
+ + c_offset;
- if (ch->meram) {
- struct sh_mobile_meram_info *mdev;
-
- mdev = priv->meram_dev;
- mdev->ops->meram_update(mdev, ch->meram,
- base_addr_y, base_addr_c,
- &base_addr_y, &base_addr_c);
- }
+ if (ch->cache)
+ sh_mobile_meram_cache_update(priv->meram_dev, ch->cache,
+ base_addr_y, base_addr_c,
+ &base_addr_y, &base_addr_c);
ch->base_addr_y = base_addr_y;
ch->base_addr_c = base_addr_c;
+ ch->pan_y_offset = y_offset;
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
if (ch->format->yuv)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
+ ldrcntr = lcdc_read(priv, _LDRCNTR);
if (lcdc_chan_is_sublcd(ch))
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
else
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
- ch->pan_offset = new_pan_offset;
sh_mobile_lcdc_deferred_io_touch(info);
return 0;
}
-static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
+ struct sh_mobile_lcdc_chan *ch = info->par;
int retval;
switch (cmd) {
case FBIO_WAITFORVSYNC:
- retval = sh_mobile_wait_for_vsync(info->par);
+ retval = sh_mobile_lcdc_wait_for_vsync(ch);
break;
default:
@@ -1158,7 +1923,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
* Locking: both .fb_release() and .fb_open() are called with info->lock held if
* user == 1, or with console sem held, if user == 0.
*/
-static int sh_mobile_release(struct fb_info *info, int user)
+static int sh_mobile_lcdc_release(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1179,7 +1944,7 @@ static int sh_mobile_release(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_open(struct fb_info *info, int user)
+static int sh_mobile_lcdc_open(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1192,7 +1957,8 @@ static int sh_mobile_open(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *p = ch->lcdc;
@@ -1200,9 +1966,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
unsigned int best_xres = 0;
unsigned int best_yres = 0;
unsigned int i;
-
- if (var->xres > MAX_XRES || var->yres > MAX_YRES)
- return -EINVAL;
+ int ret;
/* If board code provides us with a list of available modes, make sure
* we use one of them. Find the mode closest to the requested one. The
@@ -1237,73 +2001,9 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
var->yres = best_yres;
}
- /* Make sure the virtual resolution is at least as big as the visible
- * resolution.
- */
- if (var->xres_virtual < var->xres)
- var->xres_virtual = var->xres;
- if (var->yres_virtual < var->yres)
- var->yres_virtual = var->yres;
-
- if (sh_mobile_format_is_fourcc(var)) {
- const struct sh_mobile_lcdc_format_info *format;
-
- format = sh_mobile_format_info(var->grayscale);
- if (format == NULL)
- return -EINVAL;
- var->bits_per_pixel = format->bpp;
-
- /* Default to RGB and JPEG color-spaces for RGB and YUV formats
- * respectively.
- */
- if (!format->yuv)
- var->colorspace = V4L2_COLORSPACE_SRGB;
- else if (var->colorspace != V4L2_COLORSPACE_REC709)
- var->colorspace = V4L2_COLORSPACE_JPEG;
- } else {
- if (var->bits_per_pixel <= 16) { /* RGB 565 */
- var->bits_per_pixel = 16;
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
- var->bits_per_pixel = 24;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
- var->bits_per_pixel = 32;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 24;
- var->transp.length = 8;
- } else
- return -EINVAL;
-
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- var->transp.msb_right = 0;
- }
-
- /* Make sure we don't exceed our allocated memory. */
- if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
- info->fix.smem_len)
- return -EINVAL;
+ ret = __sh_mobile_lcdc_check_var(var, info);
+ if (ret < 0)
+ return ret;
/* only accept the forced_fourcc for dual channel configurations */
if (p->forced_fourcc &&
@@ -1313,7 +2013,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
return 0;
}
-static int sh_mobile_set_par(struct fb_info *info)
+static int sh_mobile_lcdc_set_par(struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
int ret;
@@ -1329,9 +2029,9 @@ static int sh_mobile_set_par(struct fb_info *info)
ch->yres_virtual = info->var.yres_virtual;
if (ch->format->yuv)
- ch->pitch = info->var.xres;
+ ch->pitch = info->var.xres_virtual;
else
- ch->pitch = info->var.xres * ch->format->bpp / 8;
+ ch->pitch = info->var.xres_virtual * ch->format->bpp / 8;
ret = sh_mobile_lcdc_start(ch->lcdc);
if (ret < 0)
@@ -1383,8 +2083,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
* mode will reenable the clocks and update the screen in time,
* so it does not need this. */
if (!info->fbdefio) {
- sh_mobile_wait_for_vsync(ch);
- sh_mobile_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
}
sh_mobile_lcdc_clk_off(p);
}
@@ -1402,12 +2102,12 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
.fb_blank = sh_mobile_lcdc_blank,
- .fb_pan_display = sh_mobile_fb_pan_display,
- .fb_ioctl = sh_mobile_ioctl,
- .fb_open = sh_mobile_open,
- .fb_release = sh_mobile_release,
- .fb_check_var = sh_mobile_check_var,
- .fb_set_par = sh_mobile_set_par,
+ .fb_pan_display = sh_mobile_lcdc_pan,
+ .fb_ioctl = sh_mobile_lcdc_ioctl,
+ .fb_open = sh_mobile_lcdc_open,
+ .fb_release = sh_mobile_lcdc_release,
+ .fb_check_var = sh_mobile_lcdc_check_var,
+ .fb_set_par = sh_mobile_lcdc_set_par,
};
static void
@@ -1514,19 +2214,24 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
else
info->fix.visual = FB_VISUAL_TRUECOLOR;
- if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
- ch->format->fourcc == V4L2_PIX_FMT_NV21)
+ switch (ch->format->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
info->fix.ypanstep = 2;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ info->fix.xpanstep = 2;
+ }
/* Initialize variable screen information using the first mode as
- * default. The default Y virtual resolution is twice the panel size to
- * allow for double-buffering.
+ * default.
*/
var = &info->var;
fb_videomode_to_var(var, mode);
var->width = ch->cfg->panel_cfg.width;
var->height = ch->cfg->panel_cfg.height;
- var->yres_virtual = var->yres * 2;
+ var->xres_virtual = ch->xres_virtual;
+ var->yres_virtual = ch->yres_virtual;
var->activate = FB_ACTIVATE_NOW;
/* Use the legacy API by default for RGB formats, and the FOURCC API
@@ -1537,7 +2242,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
else
var->grayscale = ch->format->fourcc;
- ret = sh_mobile_check_var(var, info);
+ ret = sh_mobile_lcdc_check_var(var, info);
if (ret)
return ret;
@@ -1712,15 +2417,27 @@ static const struct fb_videomode default_720p __devinitconst = {
static int sh_mobile_lcdc_remove(struct platform_device *pdev)
{
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- int i;
+ unsigned int i;
fb_unregister_client(&priv->notifier);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+ sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
sh_mobile_lcdc_stop(priv);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ sh_mobile_lcdc_overlay_fb_cleanup(ovl);
+
+ if (ovl->fb_mem)
+ dma_free_coherent(&pdev->dev, ovl->fb_size,
+ ovl->fb_mem, ovl->dma_handle);
+ }
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
@@ -1737,8 +2454,11 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
}
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
- if (priv->ch[i].bl)
- sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
+ struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
+
+ if (ch->bl)
+ sh_mobile_lcdc_bl_remove(ch->bl);
+ mutex_destroy(&ch->open_lock);
}
if (priv->dot_clk) {
@@ -1796,6 +2516,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
}
static int __devinit
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
+ struct sh_mobile_lcdc_overlay *ovl)
+{
+ const struct sh_mobile_lcdc_format_info *format;
+ int ret;
+
+ if (ovl->cfg->fourcc == 0)
+ return 0;
+
+ /* Validate the format. */
+ format = sh_mobile_format_info(ovl->cfg->fourcc);
+ if (format == NULL) {
+ dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+ return -EINVAL;
+ }
+
+ ovl->enabled = false;
+ ovl->mode = LCDC_OVERLAY_BLEND;
+ ovl->alpha = 255;
+ ovl->rop3 = 0;
+ ovl->pos_x = 0;
+ ovl->pos_y = 0;
+
+ /* The default Y virtual resolution is twice the panel size to allow for
+ * double-buffering.
+ */
+ ovl->format = format;
+ ovl->xres = ovl->cfg->max_xres;
+ ovl->xres_virtual = ovl->xres;
+ ovl->yres = ovl->cfg->max_yres;
+ ovl->yres_virtual = ovl->yres * 2;
+
+ if (!format->yuv)
+ ovl->pitch = ovl->xres_virtual * format->bpp / 8;
+ else
+ ovl->pitch = ovl->xres_virtual;
+
+ /* Allocate frame buffer memory. */
+ ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
+ * format->bpp / 8 * 2;
+ ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
+ &ovl->dma_handle, GFP_KERNEL);
+ if (!ovl->fb_mem) {
+ dev_err(priv->dev, "unable to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_mobile_lcdc_overlay_fb_init(ovl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int __devinit
sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
struct sh_mobile_lcdc_chan *ch)
{
@@ -1854,7 +2629,9 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
num_modes = cfg->num_modes;
}
- /* Use the first mode as default. */
+ /* Use the first mode as default. The default Y virtual resolution is
+ * twice the panel size to allow for double-buffering.
+ */
ch->format = format;
ch->xres = mode->xres;
ch->xres_virtual = mode->xres;
@@ -1863,10 +2640,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
if (!format->yuv) {
ch->colorspace = V4L2_COLORSPACE_SRGB;
- ch->pitch = ch->xres * format->bpp / 8;
+ ch->pitch = ch->xres_virtual * format->bpp / 8;
} else {
ch->colorspace = V4L2_COLORSPACE_REC709;
- ch->pitch = ch->xres;
+ ch->pitch = ch->xres_virtual;
}
ch->display.width = cfg->panel_cfg.width;
@@ -1952,7 +2729,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
}
init_waitqueue_head(&ch->frame_end_wait);
init_completion(&ch->vsync_completion);
- ch->pan_offset = 0;
/* probe the backlight is there is one defined */
if (ch->cfg->bl_info.max_brightness)
@@ -2003,6 +2779,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ ovl->cfg = &pdata->overlays[i];
+ ovl->channel = &priv->ch[0];
+
+ error = sh_mobile_lcdc_overlay_init(priv, ovl);
+ if (error)
+ goto err1;
+ }
+
error = sh_mobile_lcdc_start(priv);
if (error) {
dev_err(&pdev->dev, "unable to start hardware\n");
@@ -2017,6 +2804,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ error = sh_mobile_lcdc_overlay_fb_register(ovl);
+ if (error)
+ goto err1;
+ }
+
/* Failure ignored */
priv->notifier.notifier_call = sh_mobile_lcdc_notify;
fb_register_client(&priv->notifier);
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 5c3bddd2cb72..0f92f6544b94 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -47,6 +47,7 @@ struct sh_mobile_lcdc_entity {
/*
* struct sh_mobile_lcdc_chan - LCDC display channel
*
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
* @base_addr_y: Frame buffer viewport base address (luma component)
* @base_addr_c: Frame buffer viewport base address (chroma component)
* @pitch: Frame buffer line pitch
@@ -59,7 +60,7 @@ struct sh_mobile_lcdc_chan {
unsigned long *reg_offs;
unsigned long ldmt1r_value;
unsigned long enabled; /* ME and SE in LDCNT2R */
- void *meram;
+ void *cache;
struct mutex open_lock; /* protects the use counter */
int use_count;
@@ -68,7 +69,7 @@ struct sh_mobile_lcdc_chan {
unsigned long fb_size;
dma_addr_t dma_handle;
- unsigned long pan_offset;
+ unsigned long pan_y_offset;
unsigned long frame_end;
wait_queue_head_t frame_end_wait;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 82ba830bf95d..7a0ba8bb3fbe 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -194,13 +195,28 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
}
/* -----------------------------------------------------------------------------
- * Allocation
+ * MERAM allocation and free
+ */
+
+static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
+{
+ return gen_pool_alloc(priv->pool, size);
+}
+
+static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
+ size_t size)
+{
+ gen_pool_free(priv->pool, mem, size);
+}
+
+/* -----------------------------------------------------------------------------
+ * LCDC cache planes allocation, init, cleanup and free
*/
/* Allocate ICBs and MERAM for a plane. */
-static int __meram_alloc(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- size_t size)
+static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ size_t size)
{
unsigned long mem;
unsigned long idx;
@@ -215,7 +231,7 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
return -ENOMEM;
plane->marker = &priv->icbs[idx];
- mem = gen_pool_alloc(priv->pool, size * 1024);
+ mem = meram_alloc(priv, size * 1024);
if (mem == 0)
return -ENOMEM;
@@ -229,11 +245,11 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
}
/* Free ICBs and MERAM for a plane. */
-static void __meram_free(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
{
- gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
- plane->marker->size * 1024);
+ meram_free(priv, priv->meram + plane->marker->offset,
+ plane->marker->size * 1024);
__clear_bit(plane->marker->index, &priv->used_icb);
__clear_bit(plane->cache->index, &priv->used_icb);
@@ -248,62 +264,6 @@ static int is_nvcolor(int cspace)
return 0;
}
-/* Allocate memory for the ICBs and mark them as used. */
-static struct sh_mobile_meram_fb_cache *
-meram_alloc(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_cfg *cfg,
- int pixelformat)
-{
- struct sh_mobile_meram_fb_cache *cache;
- unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
- int ret;
-
- if (cfg->icb[0].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- if (nplanes == 2 && cfg->icb[1].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- cache = kzalloc(sizeof(*cache), GFP_KERNEL);
- if (cache == NULL)
- return ERR_PTR(-ENOMEM);
-
- cache->nplanes = nplanes;
-
- ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
- if (ret < 0)
- goto error;
-
- cache->planes[0].marker->current_reg = 1;
- cache->planes[0].marker->pixelformat = pixelformat;
-
- if (cache->nplanes == 1)
- return cache;
-
- ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
- if (ret < 0) {
- __meram_free(priv, &cache->planes[0]);
- goto error;
- }
-
- return cache;
-
-error:
- kfree(cache);
- return ERR_PTR(-ENOMEM);
-}
-
-/* Unmark the specified ICB as used. */
-static void meram_free(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_cache *cache)
-{
- __meram_free(priv, &cache->planes[0]);
- if (cache->nplanes == 2)
- __meram_free(priv, &cache->planes[1]);
-
- kfree(cache);
-}
-
/* Set the next address to fetch. */
static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_fb_cache *cache,
@@ -355,10 +315,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
/* Initialize MERAM. */
-static int meram_init(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- unsigned int xres, unsigned int yres,
- unsigned int *out_pitch)
+static int meram_plane_init(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ unsigned int xres, unsigned int yres,
+ unsigned int *out_pitch)
{
struct sh_mobile_meram_icb *marker = plane->marker;
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
@@ -427,8 +387,8 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
return 0;
}
-static void meram_deinit(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
{
/* disable ICB */
meram_write_icb(priv->base, plane->cache->index, MExxCTL,
@@ -441,20 +401,82 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
}
/* -----------------------------------------------------------------------------
- * Registration/unregistration
+ * MERAM operations
*/
-static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned int *pitch)
+unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
+ size_t size)
+{
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+
+ return meram_alloc(priv, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);
+
+void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
+ size_t size)
+{
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+
+ meram_free(priv, mem, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_free);
+
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_cache_alloc(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_cfg *cfg,
+ int pixelformat)
+{
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+ struct sh_mobile_meram_fb_cache *cache;
+ int ret;
+
+ cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+ if (cache == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cache->nplanes = nplanes;
+
+ ret = meram_plane_alloc(priv, &cache->planes[0],
+ cfg->icb[0].meram_size);
+ if (ret < 0)
+ goto error;
+
+ cache->planes[0].marker->current_reg = 1;
+ cache->planes[0].marker->pixelformat = pixelformat;
+
+ if (cache->nplanes == 1)
+ return cache;
+
+ ret = meram_plane_alloc(priv, &cache->planes[1],
+ cfg->icb[1].meram_size);
+ if (ret < 0) {
+ meram_plane_free(priv, &cache->planes[0]);
+ goto error;
+ }
+
+ return cache;
+
+error:
+ kfree(cache);
+ return ERR_PTR(-ENOMEM);
+}
+
+void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat, unsigned int *pitch)
{
struct sh_mobile_meram_fb_cache *cache;
struct sh_mobile_meram_priv *priv = pdata->priv;
struct platform_device *pdev = pdata->pdev;
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
unsigned int out_pitch;
+ if (priv == NULL)
+ return ERR_PTR(-ENODEV);
+
if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
pixelformat != SH_MOBILE_MERAM_PF_RGB)
@@ -469,10 +491,16 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
return ERR_PTR(-EINVAL);
}
+ if (cfg->icb[0].meram_size == 0)
+ return ERR_PTR(-EINVAL);
+
+ if (nplanes == 2 && cfg->icb[1].meram_size == 0)
+ return ERR_PTR(-EINVAL);
+
mutex_lock(&priv->lock);
/* We now register the ICBs and allocate the MERAM regions. */
- cache = meram_alloc(priv, cfg, pixelformat);
+ cache = meram_cache_alloc(priv, cfg, pixelformat);
if (IS_ERR(cache)) {
dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
PTR_ERR(cache));
@@ -480,42 +508,50 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
}
/* initialize MERAM */
- meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
+ meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
*pitch = out_pitch;
if (pixelformat == SH_MOBILE_MERAM_PF_NV)
- meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
- &out_pitch);
+ meram_plane_init(priv, &cache->planes[1],
+ xres, (yres + 1) / 2, &out_pitch);
else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
- meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
- &out_pitch);
+ meram_plane_init(priv, &cache->planes[1],
+ 2 * xres, (yres + 1) / 2, &out_pitch);
err:
mutex_unlock(&priv->lock);
return cache;
}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);
-static void
-sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
+void
+sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
{
struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
- /* deinit & free */
- meram_deinit(priv, &cache->planes[0]);
- if (cache->nplanes == 2)
- meram_deinit(priv, &cache->planes[1]);
+ /* Cleanup and free. */
+ meram_plane_cleanup(priv, &cache->planes[0]);
+ meram_plane_free(priv, &cache->planes[0]);
+
+ if (cache->nplanes == 2) {
+ meram_plane_cleanup(priv, &cache->planes[1]);
+ meram_plane_free(priv, &cache->planes[1]);
+ }
- meram_free(priv, cache);
+ kfree(cache);
mutex_unlock(&priv->lock);
}
-
-static void
-sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
- unsigned long base_addr_y, unsigned long base_addr_c,
- unsigned long *icb_addr_y, unsigned long *icb_addr_c)
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);
+
+void
+sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c)
{
struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv = pdata->priv;
@@ -527,13 +563,7 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
mutex_unlock(&priv->lock);
}
-
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
- .module = THIS_MODULE,
- .meram_register = sh_mobile_meram_register,
- .meram_unregister = sh_mobile_meram_unregister,
- .meram_update = sh_mobile_meram_update,
-};
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
/* -----------------------------------------------------------------------------
* Power management
@@ -624,7 +654,6 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
for (i = 0; i < MERAM_ICB_NUM; ++i)
priv->icbs[i].index = i;
- pdata->ops = &sh_mobile_meram_ops;
pdata->priv = priv;
pdata->pdev = pdev;
diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c
index 26f864289498..5533a32c6ca1 100644
--- a/drivers/video/smscufx.c
+++ b/drivers/video/smscufx.c
@@ -904,7 +904,7 @@ static ssize_t ufx_ops_write(struct fb_info *info, const char __user *buf,
result = fb_sys_write(info, buf, count, ppos);
if (result > 0) {
- int start = max((int)(offset / info->fix.line_length) - 1, 0);
+ int start = max((int)(offset / info->fix.line_length), 0);
int lines = min((u32)((result / info->fix.line_length) + 1),
(u32)info->var.yres);
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index 90a2e30272ad..2f6b2b835f88 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -1567,6 +1567,18 @@ static void w100_suspend(u32 mode)
val = readl(remapped_regs + mmPLL_CNTL);
val |= 0x00000004; /* bit2=1 */
writel(val, remapped_regs + mmPLL_CNTL);
+
+ writel(0x00000000, remapped_regs + mmLCDD_CNTL1);
+ writel(0x00000000, remapped_regs + mmLCDD_CNTL2);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL1);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL2);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL3);
+
+ val = readl(remapped_regs + mmMEM_EXT_CNTL);
+ val |= 0xF0000000;
+ val &= ~(0x00000001);
+ writel(val, remapped_regs + mmMEM_EXT_CNTL);
+
writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
}
}